├── .github └── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── .gitignore ├── .jenkins ├── Jenkinsfile.deploy └── Jenkinsfile.snapshot ├── Common ├── build.gradle └── src │ └── main │ ├── java │ └── com │ │ └── hypherionmc │ │ └── sdlink │ │ ├── SDLinkConstants.java │ │ ├── api │ │ ├── accounts │ │ │ ├── DiscordAuthor.java │ │ │ ├── DiscordUser.java │ │ │ └── MinecraftAccount.java │ │ ├── events │ │ │ ├── SDLinkReadyEvent.java │ │ │ ├── SlashCommandRegistrationEvent.java │ │ │ └── VerificationEvent.java │ │ └── messaging │ │ │ ├── MessageContext.java │ │ │ ├── MessageDestination.java │ │ │ ├── MessageType.java │ │ │ ├── Result.java │ │ │ └── discord │ │ │ ├── DiscordMessage.java │ │ │ └── DiscordMessageBuilder.java │ │ ├── client │ │ └── ClientEvents.java │ │ ├── compat │ │ ├── MModeCompat.java │ │ └── rolesync │ │ │ ├── RoleSync.java │ │ │ └── impl │ │ │ ├── AbstractRoleSyncer.java │ │ │ ├── FTBRankSync.java │ │ │ ├── LuckPermsSync.java │ │ │ └── PlayerRolesSync.java │ │ ├── core │ │ ├── config │ │ │ ├── AppliesTo.java │ │ │ ├── AvatarType.java │ │ │ ├── SDLinkCompatConfig.java │ │ │ ├── SDLinkConfig.java │ │ │ ├── SDLinkRelayConfig.java │ │ │ ├── TriBoolean.java │ │ │ └── impl │ │ │ │ ├── AccessControl.java │ │ │ │ ├── BotConfigSettings.java │ │ │ │ ├── ChannelWebhookConfig.java │ │ │ │ ├── ChatSettingsConfig.java │ │ │ │ ├── GeneralConfigSettings.java │ │ │ │ ├── MessageChannelConfig.java │ │ │ │ ├── MessageFormatting.java │ │ │ │ ├── MessageIgnoreConfig.java │ │ │ │ ├── MinecraftCommands.java │ │ │ │ ├── RelayMessageConfig.java │ │ │ │ ├── RelayServerConfig.java │ │ │ │ ├── TriggerCommandsConfig.java │ │ │ │ └── compat │ │ │ │ ├── CommonCompat.java │ │ │ │ ├── MaintenanceModeCompat.java │ │ │ │ ├── PlayerReviveCompat.java │ │ │ │ ├── RoleSyncCompat.java │ │ │ │ └── VanishCompat.java │ │ ├── database │ │ │ ├── HiddenPlayers.java │ │ │ └── SDLinkAccount.java │ │ ├── discord │ │ │ ├── BotController.java │ │ │ ├── SDLWebhookServerMember.java │ │ │ ├── commands │ │ │ │ ├── CommandManager.java │ │ │ │ └── slash │ │ │ │ │ ├── SDLinkSlashCommand.java │ │ │ │ │ ├── general │ │ │ │ │ ├── HelpSlashCommand.java │ │ │ │ │ ├── PlayerListSlashCommand.java │ │ │ │ │ └── ServerStatusSlashCommand.java │ │ │ │ │ ├── hide │ │ │ │ │ ├── HiddenPlayersCommand.java │ │ │ │ │ ├── HidePlayerCommand.java │ │ │ │ │ └── UnhidePlayerCommand.java │ │ │ │ │ ├── setup │ │ │ │ │ ├── ReloadCacheCommand.java │ │ │ │ │ └── SetChannelCommand.java │ │ │ │ │ └── verification │ │ │ │ │ ├── StaffUnverifyCommand.java │ │ │ │ │ ├── StaffVerifyAccountCommand.java │ │ │ │ │ ├── UnverifyAccountSlashCommand.java │ │ │ │ │ ├── VerifyAccountCommand.java │ │ │ │ │ └── ViewVerifiedAccounts.java │ │ │ ├── events │ │ │ │ └── DiscordEventHandler.java │ │ │ └── hooks │ │ │ │ ├── BotReadyHooks.java │ │ │ │ ├── DiscordMessageHooks.java │ │ │ │ ├── DiscordRoleHooks.java │ │ │ │ └── MinecraftCommandHook.java │ │ ├── editor │ │ │ ├── ConfigEditorClient.java │ │ │ └── ConfigEditorWSEvents.java │ │ ├── experimental │ │ │ └── ExperimentalFeatures.java │ │ ├── jsondb │ │ │ ├── JsonDatabase.java │ │ │ └── annotations │ │ │ │ ├── Document.java │ │ │ │ └── Id.java │ │ ├── managers │ │ │ ├── CacheManager.java │ │ │ ├── ChannelManager.java │ │ │ ├── DatabaseManager.java │ │ │ ├── EmbedManager.java │ │ │ ├── HiddenPlayersManager.java │ │ │ ├── PermissionChecker.java │ │ │ ├── RoleManager.java │ │ │ ├── SpamManager.java │ │ │ └── WebhookManager.java │ │ ├── messaging │ │ │ ├── SDLinkWebhookClientBuilder.java │ │ │ └── embeds │ │ │ │ └── DiscordEmbed.java │ │ ├── relay │ │ │ ├── DataMessage.java │ │ │ ├── RelayMessage.java │ │ │ └── SDLinkRelayClient.java │ │ └── services │ │ │ └── SDLinkPlatform.java │ │ ├── networking │ │ ├── MentionsSyncPacket.java │ │ └── SDLinkNetworking.java │ │ ├── platform │ │ ├── SDLinkFakePlayer.java │ │ └── SDLinkMCPlatform.java │ │ ├── server │ │ ├── SDLinkMinecraftBridge.java │ │ ├── ServerEvents.java │ │ └── commands │ │ │ ├── ConfigEditorCommand.java │ │ │ ├── DiscordCommand.java │ │ │ ├── DiscordVerifyCommand.java │ │ │ ├── HidePlayerCommand.java │ │ │ ├── ReloadBotCommand.java │ │ │ ├── ReloadEmbedsCommand.java │ │ │ ├── UnhidePlayerCommand.java │ │ │ └── WhoisCommand.java │ │ └── util │ │ ├── DestinationHolder.java │ │ ├── EncryptionUtil.java │ │ ├── LogReader.java │ │ ├── MessageUtil.java │ │ ├── PKUtil.java │ │ ├── SDLinkChatUtils.java │ │ ├── SDLinkUtils.java │ │ ├── SystemUtils.java │ │ ├── ThreadedEventManager.java │ │ ├── configeditor │ │ └── SocketResponse.java │ │ └── translations │ │ ├── Text.java │ │ └── TranslationManager.java │ └── resources │ └── assets │ └── sdlink │ └── lang │ ├── en_us.json │ ├── es_es.json │ ├── fr_fr.json │ ├── pt_br.json │ └── zh_tw.json ├── LICENSE ├── ModLoaders ├── build.gradle └── src │ └── main │ ├── java │ └── com │ │ └── hypherionmc │ │ └── sdlink │ │ └── loaders │ │ ├── fabric │ │ ├── SDLinkFabric.java │ │ └── SDLinkFabricClient.java │ │ ├── forge │ │ └── SDLinkForge.java │ │ └── neoforge │ │ └── SDLinkNeoForge.java │ └── resources │ ├── META-INF │ ├── mods.toml │ └── neoforge.mods.toml │ ├── fabric.mod.json │ └── pack.mcmeta ├── PluginVersion ├── build.gradle └── src │ └── main │ ├── java │ └── com │ │ └── hypherionmc │ │ └── sdlink │ │ └── loaders │ │ └── paper │ │ ├── SDLinkPaper.java │ │ └── SDLinkPaperBootstrap.java │ └── resources │ └── paper-plugin.yml ├── build.gradle ├── changelog.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── readme.md └── settings.gradle /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: "🐞 Bug Report" 2 | description: Report a bug or unexpected behavior 3 | title: "[Bug]: " 4 | labels: ["Type: Bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: "## 🐞 Bug Report\nPlease provide a clear description of the issue." 9 | 10 | - type: textarea 11 | id: description 12 | attributes: 13 | label: "🐞 Description" 14 | description: "Provide a detailed description about the bug you are experiencing" 15 | placeholder: "When I launch my game, it sold my kidney on Facebook Marketplace.... etc" 16 | validations: 17 | required: true 18 | 19 | - type: textarea 20 | id: reproduction_steps 21 | attributes: 22 | label: "🔄 Steps to reproduce" 23 | description: "List the steps to reproduce this bug." 24 | placeholder: "1. Do this... 2. Click that... 3. See error" 25 | validations: 26 | required: true 27 | 28 | - type: textarea 29 | id: expected_behavior 30 | attributes: 31 | label: "✅ Expected behavior" 32 | description: "What should have happened?" 33 | placeholder: "I expected that..." 34 | validations: 35 | required: true 36 | 37 | - type: textarea 38 | id: actual_behavior 39 | attributes: 40 | label: "❌ Actual behavior" 41 | description: "What actually happened?" 42 | placeholder: "Instead, I saw..." 43 | validations: 44 | required: true 45 | 46 | - type: input 47 | id: minecraft_version 48 | attributes: 49 | label: "🎮 Minecraft Version" 50 | description: "Which version of Minecraft are you using?" 51 | placeholder: "e.g., 1.20.1" 52 | validations: 53 | required: true 54 | 55 | - type: input 56 | id: mod_version 57 | attributes: 58 | label: "🛠️ Mod Version" 59 | description: "Which version of the mod are you using?" 60 | placeholder: "e.g., 2.3.0" 61 | validations: 62 | required: true 63 | 64 | - type: dropdown 65 | id: mod_loader 66 | attributes: 67 | label: "📦 Mod Loader" 68 | description: "Which mod loader are you using?" 69 | options: 70 | - Forge 71 | - Fabric 72 | - Quilt 73 | - NeoForge 74 | - Paper 75 | validations: 76 | required: true 77 | 78 | - type: input 79 | id: mod_loader_version 80 | attributes: 81 | label: "🔧 Mod Loader Version" 82 | description: "Which version of the mod loader are you using?" 83 | placeholder: "e.g., 47.1.3 (for Forge)" 84 | validations: 85 | required: true 86 | 87 | - type: input 88 | id: craterlib_version 89 | attributes: 90 | label: "📚 CraterLib Version" 91 | description: "Which version of CraterLib are you using?" 92 | placeholder: "e.g., 1.5.2" 93 | validations: 94 | required: true 95 | 96 | - type: textarea 97 | id: logs 98 | attributes: 99 | label: "📜 Logs & Screenshots" 100 | description: "Upload logs, screenshots, or error messages." 101 | placeholder: "Drag and drop files here or paste logs." 102 | validations: 103 | required: false 104 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 💬 Join our Discord 4 | url: https://discord.firstdark.dev 5 | about: Need help or want to discuss something? Join our Discord community! 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: "✨ Feature Request" 2 | description: Suggest a new feature or improvement 3 | title: "[Feature]: " 4 | labels: ["Type: Enhancement"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: "## ✨ Feature Request\nDescribe the feature or improvement." 9 | 10 | - type: textarea 11 | id: feature_description 12 | attributes: 13 | label: "🚀 Description" 14 | description: "What would you like to see?" 15 | placeholder: "I want to add..." 16 | 17 | - type: textarea 18 | id: use_case 19 | attributes: 20 | label: "💡 Use Case" 21 | description: "Why would this be useful?" 22 | placeholder: "This feature helps because..." 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea 9 | *.iws 10 | *.iml 11 | *.ipr 12 | out/ 13 | !**/src/main/**/out/ 14 | !**/src/test/**/out/ 15 | 16 | ### Eclipse ### 17 | .apt_generated 18 | .classpath 19 | .factorypath 20 | .project 21 | .settings 22 | .springBeans 23 | .sts4-cache 24 | bin/ 25 | !**/src/main/**/bin/ 26 | !**/src/test/**/bin/ 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /dist/ 32 | /nbdist/ 33 | /.nb-gradle/ 34 | 35 | ### VS Code ### 36 | .vscode/ 37 | 38 | ### Mac OS ### 39 | .DS_Store 40 | 41 | ### Project ### 42 | artifacts 43 | run 44 | logs -------------------------------------------------------------------------------- /.jenkins/Jenkinsfile.deploy: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | docker { 4 | image "registry.firstdark.dev/java17:latest" 5 | alwaysPull true 6 | args '-v gradle-cache:/home/gradle/.gradle' 7 | } 8 | } 9 | 10 | environment { 11 | GRADLE_USER_HOME = '/home/gradle/.gradle' 12 | } 13 | 14 | stages { 15 | stage("Notify Discord") { 16 | steps { 17 | discordSend webhookURL: env.FDD_WH_ADMIN, 18 | title: "Deploy Started: SDLink Universal Deploy #${BUILD_NUMBER}", 19 | link: env.BUILD_URL, 20 | result: 'SUCCESS', 21 | description: "Build: [${BUILD_NUMBER}](${env.BUILD_URL})" 22 | } 23 | } 24 | stage("Prepare") { 25 | steps { 26 | sh "chmod +x ./gradlew" 27 | sh "./gradlew clean" 28 | } 29 | } 30 | stage("Publish") { 31 | steps { 32 | sh "./gradlew publishMod -Prelease=true" 33 | } 34 | } 35 | 36 | stage("Publish Maven") { 37 | steps { 38 | sh "./gradlew publish -Prelease=true" 39 | } 40 | } 41 | } 42 | post { 43 | always { 44 | sh "./gradlew --stop" 45 | deleteDir() 46 | 47 | discordSend webhookURL: env.FDD_WH_ADMIN, 48 | title: "SDLink Universal Deploy #${BUILD_NUMBER}", 49 | link: env.BUILD_URL, 50 | result: currentBuild.currentResult, 51 | description: "Build: [${BUILD_NUMBER}](${env.BUILD_URL})\nStatus: ${currentBuild.currentResult}" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.jenkins/Jenkinsfile.snapshot: -------------------------------------------------------------------------------- 1 | def projectName = "Simple Discord Link"; 2 | def projectIcon = "https://cdn.modrinth.com/data/Sh0YauEf/icon.png"; 3 | 4 | pipeline { 5 | agent { 6 | docker { 7 | image "registry.firstdark.dev/java17:latest" 8 | alwaysPull true 9 | args '-v gradle-cache:/home/gradle/.gradle' 10 | } 11 | } 12 | 13 | environment { 14 | GRADLE_USER_HOME = '/home/gradle/.gradle' 15 | } 16 | 17 | stages { 18 | stage('Checkout') { 19 | steps { 20 | scmSkip(deleteBuild: true, skipPattern:'.*\\[ci skip\\].*') 21 | } 22 | } 23 | 24 | stage("Notify Discord") { 25 | steps { 26 | discordSend webhookURL: env.SSS_WEBHOOK, 27 | title: "Deploy Started: ${projectName} Universal Deploy #${BUILD_NUMBER}", 28 | link: env.BUILD_URL, 29 | result: 'SUCCESS', 30 | description: "Build: [${BUILD_NUMBER}](${env.BUILD_URL})" 31 | } 32 | } 33 | 34 | stage("Build") { 35 | steps { 36 | sh "chmod +x ./gradlew" 37 | sh "./gradlew clean build -PreleaseType=snapshot" 38 | } 39 | } 40 | } 41 | 42 | post { 43 | always { 44 | sh "./gradlew --stop" 45 | archiveArtifacts artifacts: 'artifacts/*.jar' 46 | 47 | fddsnapshotter apiKey: env.PLATFORM_KEY, 48 | projectSlug: "sdlink", 49 | projectName: "${projectName}", 50 | projectIcon: "${projectIcon}", 51 | modLoaders: "forge|neoforge|fabric|quilt|paper", 52 | minecraftVersions: "1.18.2|1.19.2|1.19.3|1.19.4|1.20|1.20.1|1.20.2|1.20.3|1.20.4|1.21|1.21.1|1.21.3|1.21.4|1.21.5|1.21.6|1.21.7|1.21.8|1.21.9|1.21.10", 53 | type: "snapshot", 54 | dependsOn: "required:craterlib", 55 | failWebhook: env.SSS_WEBHOOK, 56 | publishWebhooks: "${env.SSS_WEBHOOK}|${env.FDD_WH}" 57 | 58 | deleteDir() 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Common/build.gradle: -------------------------------------------------------------------------------- 1 | jar.enabled = false 2 | shadowJar.enabled = false 3 | publishMod.enabled = false 4 | publishCurseforge.enabled = false 5 | publishModrinth.enabled = false 6 | publishNightbloom.enabled = false -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/SDLinkConstants.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public final class SDLinkConstants { 7 | 8 | public static final String MOD_ID = "sdlink"; 9 | public static final String MOD_NAME = "Simple Discord Link"; 10 | public static final Logger LOGGER = LoggerFactory.getLogger(MOD_NAME); 11 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/api/accounts/DiscordAuthor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.api.accounts; 6 | 7 | import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; 8 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 9 | import com.hypherionmc.sdlink.core.services.SDLinkPlatform; 10 | import com.hypherionmc.sdlink.util.SDLinkChatUtils; 11 | import lombok.Getter; 12 | import lombok.Setter; 13 | import net.dv8tion.jda.api.entities.Role; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | /** 17 | * @author HypherionSA 18 | * Represents a Message Author for messages sent from Minecraft to Discord 19 | */ 20 | @Getter 21 | public final class DiscordAuthor { 22 | 23 | private String displayName; 24 | private String avatar; 25 | private final boolean isServer; 26 | private String username; 27 | private String uuid; 28 | private BridgedGameProfile profile; 29 | String realPlayerAvatar = ""; 30 | String realPlayerName = ""; 31 | @Setter private int color = Role.DEFAULT_COLOR_RAW; 32 | 33 | /** 34 | * Internal. Use {@link #of(String, String, String)} 35 | * 36 | * @param displayName The Username of the Author 37 | * @param avatar The avatar URL of the Author 38 | * @param isServer Is the Author the Minecraft Server 39 | */ 40 | private DiscordAuthor(String displayName, String avatar, String username, boolean isServer, String uuid) { 41 | //this.displayName = displayName.replace("_", "\\_"); 42 | this.avatar = avatar; 43 | this.username = username; 44 | this.isServer = isServer; 45 | this.uuid = uuid; 46 | this.displayName = displayName; 47 | this.profile = null; 48 | 49 | fixDisplayName(displayName); 50 | } 51 | 52 | private void fixDisplayName(String displayName) { 53 | this.displayName = this.displayName.replace("_", "\\_"); 54 | this.displayName = SDLinkChatUtils.applyFiltering( 55 | this.displayName, 56 | (i) -> i.appliesTo.appliesToUsername(i)); 57 | 58 | if (this.displayName == null || this.displayName.isEmpty()) { 59 | this.displayName = displayName; 60 | this.displayName = this.displayName.replace("_", "\\_"); 61 | } 62 | } 63 | 64 | /** 65 | * Create a new Discord Author 66 | * 67 | * @param displayName The name/Username of the Author 68 | * @param uuid The Mojang UUID of the Author 69 | * @return A constructed {@link DiscordAuthor} 70 | */ 71 | public static DiscordAuthor of(String displayName, String uuid, String username) { 72 | return new DiscordAuthor( 73 | displayName, 74 | SDLinkConfig.INSTANCE.chatConfig.playerAvatarType.resolve(SDLinkPlatform.minecraftHelper.isOnlineMode() ? uuid : username), 75 | username, 76 | false, 77 | uuid 78 | ); 79 | } 80 | 81 | public static DiscordAuthor getServer() { 82 | return new DiscordAuthor( 83 | SDLinkConfig.INSTANCE.channelsAndWebhooks.serverName, 84 | SDLinkConfig.INSTANCE.channelsAndWebhooks.serverAvatar, 85 | "server", 86 | true, 87 | "" 88 | ).setPlayerName(SDLinkConfig.INSTANCE.channelsAndWebhooks.serverName); 89 | } 90 | 91 | public static DiscordAuthor of(String displayName, String avatar, String username, boolean server) { 92 | return new DiscordAuthor( 93 | displayName, 94 | avatar, 95 | username, 96 | server, 97 | username 98 | ); 99 | } 100 | 101 | public DiscordAuthor setPlayerAvatar(String usr, String userid) { 102 | realPlayerAvatar = SDLinkConfig.INSTANCE.chatConfig.playerAvatarType.resolve(SDLinkPlatform.minecraftHelper.isOnlineMode() ? userid : usr); 103 | return this; 104 | } 105 | 106 | public DiscordAuthor setPlayerName(String name) { 107 | this.realPlayerName = name; 108 | return this; 109 | } 110 | 111 | public void overrideData(String name, String avatar) { 112 | this.displayName = name; 113 | this.avatar = avatar; 114 | this.realPlayerAvatar = avatar; 115 | fixDisplayName(name); 116 | } 117 | 118 | public DiscordAuthor setGameProfile(@Nullable BridgedGameProfile profile) { 119 | this.profile = profile; 120 | if (profile != null) { 121 | this.username = profile.getName(); 122 | this.uuid = profile.getId().toString(); 123 | } 124 | return this; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/api/accounts/DiscordUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.api.accounts; 6 | 7 | import lombok.AllArgsConstructor; 8 | import lombok.Getter; 9 | import lombok.Setter; 10 | 11 | @Setter 12 | @Getter 13 | @AllArgsConstructor(staticName = "of") 14 | public final class DiscordUser { 15 | 16 | private String effectiveName; 17 | private String avatarUrl; 18 | private long userId; 19 | private String asMention; 20 | private int roleColor; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/api/events/SDLinkReadyEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.api.events; 6 | 7 | import com.hypherionmc.craterlib.core.event.CraterEvent; 8 | 9 | public final class SDLinkReadyEvent extends CraterEvent { 10 | 11 | public SDLinkReadyEvent() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/api/events/SlashCommandRegistrationEvent.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.api.events; 2 | 3 | import com.hypherionmc.craterlib.core.event.CraterEvent; 4 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 5 | import lombok.Getter; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * @author HypherionSA 12 | * 13 | * Event that is fired before Simple Discord Link registers its slash commands 14 | */ 15 | @Getter 16 | public class SlashCommandRegistrationEvent extends CraterEvent { 17 | 18 | private final List commands = new ArrayList<>(); 19 | 20 | /** 21 | * Add your own slash command 22 | * 23 | * @param command A copy of your command class extending {@link SDLinkSlashCommand} 24 | */ 25 | public void addCommand(SDLinkSlashCommand command) { 26 | commands.add(command); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/api/events/VerificationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.api.events; 6 | 7 | import com.hypherionmc.craterlib.core.event.CraterEvent; 8 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 9 | import lombok.Getter; 10 | import lombok.RequiredArgsConstructor; 11 | 12 | /** 13 | * @author HypherionSA 14 | * Events that get triggered when the bot verification list changes 15 | */ 16 | public final class VerificationEvent { 17 | 18 | @Getter 19 | @RequiredArgsConstructor 20 | public static class PlayerVerified extends CraterEvent { 21 | private final MinecraftAccount account; 22 | } 23 | 24 | @Getter 25 | @RequiredArgsConstructor 26 | public static class PlayerUnverified extends CraterEvent { 27 | private final MinecraftAccount account; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/api/messaging/MessageDestination.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.api.messaging; 6 | 7 | /** 8 | * @author HypherionSA 9 | * Specifies to what channel a message should be delivered 10 | */ 11 | public enum MessageDestination { 12 | CHAT, 13 | EVENT, 14 | CONSOLE, 15 | OVERRIDE; 16 | 17 | public boolean isChat() { 18 | return this == CHAT; 19 | } 20 | 21 | public boolean isEvent() { 22 | return this == EVENT; 23 | } 24 | 25 | public boolean isConsole() { 26 | return this == CONSOLE; 27 | } 28 | 29 | public boolean isOverride() { 30 | return this == OVERRIDE; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/api/messaging/MessageType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.api.messaging; 6 | 7 | /** 8 | * @author HypherionSA 9 | * Used to specify the type of message being sent 10 | */ 11 | public enum MessageType { 12 | CHAT, 13 | START, 14 | STOP, 15 | JOIN, 16 | LEAVE, 17 | ADVANCEMENTS, 18 | DEATH, 19 | COMMANDS, 20 | CONSOLE, 21 | WHITELIST, 22 | CUSTOM 23 | } 24 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/api/messaging/Result.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.api.messaging; 6 | 7 | import com.hypherionmc.sdlink.util.translations.Text; 8 | import lombok.Getter; 9 | 10 | /** 11 | * @author HypherionSA 12 | * Helper Class to return the result of interactions between Discord and Minecraft 13 | */ 14 | public final class Result { 15 | 16 | private final Type type; 17 | @Getter 18 | private final String message; 19 | private Result(Type type, String message) { 20 | this.type = type; 21 | this.message = message; 22 | } 23 | 24 | public static Result success(Text text) { 25 | return Result.success(text.toString()); 26 | } 27 | 28 | public static Result success(String message) { 29 | return new Result(Type.SUCCESS, message); 30 | } 31 | 32 | public static Result error(Text text) { 33 | return Result.error(text.toString()); 34 | } 35 | 36 | public static Result error(String message) { 37 | return new Result(Type.ERROR, message); 38 | } 39 | 40 | public boolean isError() { 41 | return this.type == Type.ERROR; 42 | } 43 | 44 | enum Type { 45 | ERROR, 46 | SUCCESS 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/api/messaging/discord/DiscordMessageBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.api.messaging.discord; 6 | 7 | import com.hypherionmc.sdlink.api.accounts.DiscordAuthor; 8 | import com.hypherionmc.sdlink.api.accounts.DiscordUser; 9 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 10 | import com.hypherionmc.sdlink.api.messaging.MessageType; 11 | import com.hypherionmc.sdlink.core.config.AppliesTo; 12 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 13 | import com.hypherionmc.sdlink.core.config.impl.MessageIgnoreConfig; 14 | import com.hypherionmc.sdlink.util.SDLinkChatUtils; 15 | import lombok.Getter; 16 | 17 | import java.util.Objects; 18 | 19 | /** 20 | * @author HypherionSA 21 | * Used to construct a {@link DiscordMessage} to be sent back to discord 22 | */ 23 | @Getter 24 | public final class DiscordMessageBuilder { 25 | 26 | private final MessageType messageType; 27 | private DiscordAuthor author; 28 | private String message; 29 | private Runnable afterSend; 30 | 31 | /** 32 | * Construct a discord message 33 | * 34 | * @param messageType The type of message being sent 35 | */ 36 | public DiscordMessageBuilder(MessageType messageType) { 37 | this.messageType = messageType; 38 | } 39 | 40 | /** 41 | * Add an Author to the message 42 | */ 43 | public DiscordMessageBuilder author(DiscordAuthor author) { 44 | this.author = author; 45 | 46 | if (author.getUsername().equalsIgnoreCase("server")) { 47 | this.author = DiscordAuthor.getServer(); 48 | } 49 | 50 | if (SDLinkConfig.INSTANCE.chatConfig.useLinkedNames && !Objects.equals(this.author, DiscordAuthor.getServer()) && author.getProfile() != null) { 51 | MinecraftAccount account = MinecraftAccount.of(author.getProfile()); 52 | DiscordUser discordUser = account.getDiscordUser(); 53 | 54 | if (account != null && discordUser != null) { 55 | this.author.overrideData(discordUser.getEffectiveName(), discordUser.getAvatarUrl()); 56 | this.author.setColor(discordUser.getRoleColor()); 57 | } 58 | } 59 | 60 | if (this.messageType == MessageType.CHAT 61 | && SDLinkConfig.INSTANCE.channelsAndWebhooks.webhooks.useServerForChat 62 | && SDLinkConfig.INSTANCE.channelsAndWebhooks.webhooks.enabled 63 | && !SDLinkConfig.INSTANCE.channelsAndWebhooks.webhooks.chatWebhook.trim().isEmpty() 64 | ) { 65 | this.author = DiscordAuthor.getServer(); 66 | } 67 | 68 | return this; 69 | } 70 | 71 | /** 72 | * The Actual message that will be sent 73 | */ 74 | public DiscordMessageBuilder message(String message) { 75 | this.message = SDLinkChatUtils.applyFiltering(message, (i) -> this.messageType == MessageType.CONSOLE ? i.appliesTo.appliesToConsole(i) : i.appliesTo.appliesToChat(i)); 76 | return this; 77 | } 78 | 79 | public DiscordMessageBuilder afterSend(Runnable afterSend) { 80 | this.afterSend = afterSend; 81 | return this; 82 | } 83 | 84 | /** 85 | * Build a Discord Message ready to be sent 86 | */ 87 | public DiscordMessage build() { 88 | if (this.author == null) { 89 | this.author = DiscordAuthor.getServer(); 90 | } 91 | 92 | if (this.message == null) { 93 | this.message = ""; 94 | } 95 | 96 | return new DiscordMessage(this); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/client/ClientEvents.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.client; 2 | 3 | import com.hypherionmc.sdlink.networking.SDLinkNetworking; 4 | 5 | /** 6 | * @author HypherionSA 7 | * Client Side Functions (Mostly for mentioning Stuff) 8 | */ 9 | public final class ClientEvents { 10 | public static boolean mentionsEnabled = false; 11 | 12 | public static void init() { 13 | SDLinkNetworking.registerPackets(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/compat/MModeCompat.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.compat; 2 | 3 | import com.hypherionmc.craterlib.core.event.CraterEventBus; 4 | import com.hypherionmc.craterlib.core.event.annot.CraterEventListener; 5 | import com.hypherionmc.mmode.api.events.MaintenanceModeEvent; 6 | import com.hypherionmc.mmode.config.MaintenanceModeConfig; 7 | import com.hypherionmc.sdlink.api.accounts.DiscordAuthor; 8 | import com.hypherionmc.sdlink.api.messaging.MessageType; 9 | import com.hypherionmc.sdlink.api.messaging.discord.DiscordMessage; 10 | import com.hypherionmc.sdlink.api.messaging.discord.DiscordMessageBuilder; 11 | import com.hypherionmc.sdlink.core.config.SDLinkCompatConfig; 12 | import com.hypherionmc.sdlink.core.discord.BotController; 13 | import net.dv8tion.jda.api.OnlineStatus; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | public final class MModeCompat { 17 | 18 | public static boolean maintenanceActive = false; 19 | 20 | public static void init() { 21 | CraterEventBus.INSTANCE.registerEventListener(MModeCompat.class); 22 | } 23 | 24 | @CraterEventListener 25 | public static void maintenanceStart(MaintenanceModeEvent.MaintenanceStart event) { 26 | maintenanceActive = true; 27 | 28 | if (SDLinkCompatConfig.INSTANCE.maintenanceModeCompat.enabled && SDLinkCompatConfig.INSTANCE.maintenanceModeCompat.sendMaintenanceStart) { 29 | DiscordMessage message = new DiscordMessageBuilder(MessageType.CUSTOM) 30 | .author(DiscordAuthor.getServer()) 31 | .message(SDLinkCompatConfig.INSTANCE.maintenanceModeCompat.maintenanceStartMessage) 32 | .build(); 33 | 34 | message.sendMessage(); 35 | } 36 | 37 | if (BotController.INSTANCE.isBotReady()) { 38 | BotController.INSTANCE.getJDA().getPresence().setStatus(SDLinkCompatConfig.INSTANCE.maintenanceModeCompat.onlineStatus); 39 | } 40 | } 41 | 42 | @CraterEventListener 43 | public static void maintenanceEnd(MaintenanceModeEvent.MaintenanceEnd event) { 44 | maintenanceActive = false; 45 | 46 | if (SDLinkCompatConfig.INSTANCE.maintenanceModeCompat.enabled && SDLinkCompatConfig.INSTANCE.maintenanceModeCompat.sendMaintenanceEnd) { 47 | DiscordMessage message = new DiscordMessageBuilder(MessageType.CUSTOM) 48 | .author(DiscordAuthor.getServer()) 49 | .message(SDLinkCompatConfig.INSTANCE.maintenanceModeCompat.maintenanceEndMessage) 50 | .build(); 51 | 52 | message.sendMessage(); 53 | } 54 | 55 | if (BotController.INSTANCE.isBotReady()) { 56 | BotController.INSTANCE.getJDA().getPresence().setStatus(OnlineStatus.ONLINE); 57 | } 58 | } 59 | 60 | @Nullable 61 | public static String getMotd() { 62 | if (MaintenanceModeConfig.INSTANCE != null) { 63 | return MaintenanceModeConfig.INSTANCE.getMotd(); 64 | } 65 | 66 | return null; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/compat/rolesync/RoleSync.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.compat.rolesync; 2 | 3 | import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; 4 | import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; 5 | import com.hypherionmc.sdlink.api.accounts.DiscordUser; 6 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 7 | import com.hypherionmc.sdlink.compat.rolesync.impl.FTBRankSync; 8 | import com.hypherionmc.sdlink.compat.rolesync.impl.LuckPermsSync; 9 | import com.hypherionmc.sdlink.core.config.SDLinkCompatConfig; 10 | import com.hypherionmc.sdlink.core.discord.BotController; 11 | import net.dv8tion.jda.api.entities.Guild; 12 | import net.dv8tion.jda.api.entities.Member; 13 | import net.dv8tion.jda.api.entities.Role; 14 | 15 | import java.util.List; 16 | 17 | public final class RoleSync { 18 | 19 | public static final RoleSync INSTANCE = new RoleSync(); 20 | 21 | public void sync(BridgedPlayer p) { 22 | if (!SDLinkCompatConfig.INSTANCE.common.ftbranks && !SDLinkCompatConfig.INSTANCE.common.luckperms) 23 | return; 24 | 25 | MinecraftAccount account = MinecraftAccount.of(p.getGameProfile()); 26 | DiscordUser user = account.getDiscordUser(); 27 | if (user == null) 28 | return; 29 | 30 | Guild g = BotController.INSTANCE.getJDA().getGuilds().get(0); 31 | if (g == null) 32 | return; 33 | 34 | Member member = g.getMemberById(user.getUserId()); 35 | if (member == null) 36 | return; 37 | 38 | List roles = member.getRoles(); 39 | 40 | if (ModloaderEnvironment.INSTANCE.isModLoaded("ftbranks") && SDLinkCompatConfig.INSTANCE.common.ftbranks) { 41 | try { 42 | FTBRankSync.INSTANCE.sync(p, roles, g, member); 43 | } catch (Exception e) { 44 | e.printStackTrace(); 45 | } 46 | } 47 | 48 | if (ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") && SDLinkCompatConfig.INSTANCE.common.luckperms) { 49 | try { 50 | LuckPermsSync.INSTANCE.sync(p, roles, g, member); 51 | } catch (Exception e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | 56 | if (ModloaderEnvironment.INSTANCE.isModLoaded("player_roles") && SDLinkCompatConfig.INSTANCE.common.playerroles) { 57 | try { 58 | 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | } 64 | 65 | public void roleAddedToMember(Member member, Role role, Guild guild) { 66 | if (ModloaderEnvironment.INSTANCE.isModLoaded("ftbranks")) { 67 | FTBRankSync.INSTANCE.discordRoleAddedToMember(member, role, guild); 68 | } 69 | 70 | if (ModloaderEnvironment.INSTANCE.isModLoaded("luckperms")) { 71 | LuckPermsSync.INSTANCE.discordRoleAddedToMember(member, role, guild); 72 | } 73 | } 74 | 75 | public void roleRemovedFromMember(Member member, Role role, Guild guild, MinecraftAccount oldAccount) { 76 | if (ModloaderEnvironment.INSTANCE.isModLoaded("ftbranks")) { 77 | FTBRankSync.INSTANCE.discordRoleRemovedFromMember(member, role, guild, oldAccount); 78 | } 79 | 80 | if (ModloaderEnvironment.INSTANCE.isModLoaded("luckperms")) { 81 | LuckPermsSync.INSTANCE.discordRoleRemovedFromMember(member, role, guild, oldAccount); 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/compat/rolesync/impl/AbstractRoleSyncer.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.compat.rolesync.impl; 2 | 3 | import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; 4 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 5 | import net.dv8tion.jda.api.entities.Guild; 6 | import net.dv8tion.jda.api.entities.Member; 7 | import net.dv8tion.jda.api.entities.Role; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.List; 11 | import java.util.function.Supplier; 12 | 13 | public abstract class AbstractRoleSyncer { 14 | 15 | private final Supplier isSyncActive; 16 | boolean ignoreEvent = false; 17 | 18 | public AbstractRoleSyncer(Supplier isSyncActive) { 19 | this.isSyncActive = isSyncActive; 20 | } 21 | 22 | public abstract void sync(BridgedPlayer p, List roles, Guild guild, Member member); 23 | 24 | public void discordRoleAddedToMember(Member member, Role role, Guild guild) { 25 | if (ignoreEvent || !isSyncActive.get()) 26 | return; 27 | 28 | discordRoleChanged(member, guild, role, true); 29 | } 30 | 31 | public void discordRoleRemovedFromMember(Member member, Role role, Guild guild, MinecraftAccount oldAccount) { 32 | if (ignoreEvent || !isSyncActive.get()) 33 | return; 34 | 35 | discordRoleChanged(member, guild, role, false, oldAccount); 36 | } 37 | 38 | void discordRoleChanged(Member member, Guild guild, Role role, boolean added) { 39 | this.discordRoleChanged(member, guild, role, added, null); 40 | } 41 | 42 | abstract void discordRoleChanged(Member member, Guild guild, Role role, boolean added, @Nullable MinecraftAccount oldAccount); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/AppliesTo.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config; 2 | 3 | import com.hypherionmc.sdlink.core.config.impl.MessageIgnoreConfig; 4 | 5 | public enum AppliesTo { 6 | DISCORD, 7 | MINECRAFT; 8 | 9 | public boolean appliesToChat(MessageIgnoreConfig.Ignore ignore) { 10 | return this == DISCORD && ignore.target.isChat(); 11 | } 12 | 13 | public boolean appliesToUsername(MessageIgnoreConfig.Ignore ignore) { 14 | return this == DISCORD && ignore.target.isUsername(); 15 | } 16 | 17 | public boolean appliesToConsole(MessageIgnoreConfig.Ignore ignore) { 18 | if (ignore.target == MessageIgnoreConfig.FilterTarget.CONSOLE) 19 | return true; 20 | 21 | return !ignore.ignoreConsole; 22 | } 23 | 24 | public boolean isMinecraft() { 25 | return this == MINECRAFT; 26 | } 27 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/AvatarType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.config; 6 | 7 | /** 8 | * @author HypherionSA 9 | * The type of User Icon/Avatar that will be used for Discord Messages 10 | */ 11 | public enum AvatarType { 12 | AVATAR("https://skinatar.firstdark.dev/avatar/{uuid}"), 13 | HEAD("https://skinatar.firstdark.dev/isometric/{uuid}"), 14 | BODY("https://skinatar.firstdark.dev/body/{uuid}"), 15 | COMBO("https://skinatar.firstdark.dev/avatar/{uuid}"), 16 | CUSTOM(""); 17 | 18 | private final String url; 19 | 20 | AvatarType(String url) { 21 | this.url = url; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return this.url; 27 | } 28 | 29 | public String resolve(String uuid) { 30 | if (this == CUSTOM) { 31 | return SDLinkConfig.INSTANCE.chatConfig.customAvatarService.replace("{uuid}", uuid); 32 | } 33 | 34 | return this.url.replace("{uuid}", uuid); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/SDLinkCompatConfig.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config; 2 | 3 | import com.hypherionmc.craterlib.core.config.AbstractConfig; 4 | import com.hypherionmc.craterlib.core.config.ConfigController; 5 | import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen; 6 | import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; 7 | import com.hypherionmc.sdlink.core.config.impl.compat.*; 8 | import com.hypherionmc.sdlink.core.discord.BotController; 9 | import org.apache.commons.io.FileUtils; 10 | import shadow.hypherionmc.moonconfig.core.conversion.ObjectConverter; 11 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 12 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 13 | import shadow.hypherionmc.moonconfig.core.file.CommentedFileConfig; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | 18 | @NoConfigScreen 19 | public final class SDLinkCompatConfig extends AbstractConfig { 20 | 21 | // DO NOT REMOVE TRANSIENT HERE... OTHERWISE, THE STUPID CONFIG LIBRARY 22 | // WILL TRY TO WRITE THESE TO THE CONFIG 23 | public transient static SDLinkCompatConfig INSTANCE; 24 | public transient static int configVer = 7; 25 | public transient static boolean hasConfigLoaded = false; 26 | public transient static boolean wasReload = false; 27 | 28 | @Path("configVersion") 29 | @SpecComment("INTERNAL. DO NOT TOUCH") 30 | public int configVersion = configVer; 31 | 32 | @Path("common") 33 | @SpecComment("Disable/Enable basic integrations") 34 | public CommonCompat common = new CommonCompat(); 35 | 36 | @Path("player_revive") 37 | @SpecComment("Manage Player Revive Mod integration") 38 | public PlayerReviveCompat playerReviveCompat = new PlayerReviveCompat(); 39 | 40 | @Path("maintenance_mode") 41 | @SpecComment("Manage Maintenance Mode integration") 42 | public MaintenanceModeCompat maintenanceModeCompat = new MaintenanceModeCompat(); 43 | 44 | @Path("luckperms") 45 | @SpecComment("LuckPerms group syncing") 46 | public RoleSyncCompat luckpermsCompat = new RoleSyncCompat(); 47 | 48 | @Path("ftbranks") 49 | @SpecComment("FTB Ranks Rank syncing") 50 | public RoleSyncCompat ftbRanksCompat = new RoleSyncCompat(); 51 | 52 | @Path("playerroles") 53 | @SpecComment("Player Roles syncing") 54 | public RoleSyncCompat playerroles = new RoleSyncCompat(); 55 | 56 | @Path("vanish") 57 | public VanishCompat vanishCompat = new VanishCompat(); 58 | 59 | public SDLinkCompatConfig() { 60 | this(false); 61 | } 62 | 63 | public SDLinkCompatConfig(boolean wasReload) { 64 | super("sdlink", "simple-discord-link", "simple-discord-compat"); 65 | SDLinkCompatConfig.wasReload = wasReload; 66 | registerAndSetup(this); 67 | } 68 | 69 | @Override 70 | public void registerAndSetup(SDLinkCompatConfig config) { 71 | if (this.getConfigPath().exists() && this.getConfigPath().length() >= 2L) { 72 | this.migrateConfig(config); 73 | } else { 74 | this.saveConfig(config); 75 | } 76 | 77 | if (!wasReload) { 78 | ConfigController.register_config(this); 79 | } 80 | 81 | this.configReloaded(); 82 | } 83 | 84 | @Override 85 | public void migrateConfig(SDLinkCompatConfig conf) { 86 | CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).sync().build(); 87 | CommentedFileConfig newConfig = CommentedFileConfig.builder(getConfigPath()).sync().build(); 88 | config.load(); 89 | 90 | if (config.getInt("configVersion") == configVer) { 91 | newConfig.close(); 92 | config.close(); 93 | return; 94 | } 95 | 96 | new ObjectConverter().toConfig(conf, newConfig); 97 | ((TomlConfigFormat)this.getConfigFormat()).updateConfigValues(config, newConfig, newConfig, ""); 98 | newConfig.set("configVersion", configVer); 99 | 100 | try { 101 | FileUtils.copyFile(getConfigPath(), new File(getConfigPath().getAbsolutePath().replace(".toml", ".old"))); 102 | } catch (IOException e) { 103 | BotController.INSTANCE.getLogger().warn("Failed to create config backup.", e); 104 | } 105 | newConfig.save(); 106 | 107 | newConfig.close(); 108 | config.close(); 109 | } 110 | 111 | @Override 112 | public void configReloaded() { 113 | INSTANCE = readConfig(this); 114 | hasConfigLoaded = true; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/SDLinkRelayConfig.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config; 2 | 3 | import com.hypherionmc.craterlib.core.config.AbstractConfig; 4 | import com.hypherionmc.craterlib.core.config.ConfigController; 5 | import com.hypherionmc.craterlib.core.config.formats.TomlConfigFormat; 6 | import com.hypherionmc.sdlink.core.config.impl.RelayMessageConfig; 7 | import com.hypherionmc.sdlink.core.config.impl.RelayServerConfig; 8 | import com.hypherionmc.sdlink.core.discord.BotController; 9 | import com.hypherionmc.sdlink.core.relay.SDLinkRelayClient; 10 | import org.apache.commons.io.FileUtils; 11 | import shadow.hypherionmc.moonconfig.core.conversion.ObjectConverter; 12 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 13 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 14 | import shadow.hypherionmc.moonconfig.core.file.CommentedFileConfig; 15 | 16 | import java.io.File; 17 | import java.io.IOException; 18 | 19 | public final class SDLinkRelayConfig extends AbstractConfig { 20 | 21 | // DO NOT REMOVE TRANSIENT HERE... OTHERWISE, THE STUPID CONFIG LIBRARY 22 | // WILL TRY TO WRITE THESE TO THE CONFIG 23 | public transient static SDLinkRelayConfig INSTANCE; 24 | public transient static int configVer = 2; 25 | public transient static boolean hasConfigLoaded = false; 26 | public transient static boolean wasReload = false; 27 | 28 | @Path("configVersion") 29 | @SpecComment("INTERNAL. DO NOT TOUCH") 30 | public int configVersion = configVer; 31 | 32 | @Path("relayServer") 33 | @SpecComment("General Relay Server Config") 34 | public RelayServerConfig relayServer = new RelayServerConfig(); 35 | 36 | @Path("messageConfig") 37 | @SpecComment("Message config for relays") 38 | public RelayMessageConfig messageConfig = new RelayMessageConfig(); 39 | 40 | public SDLinkRelayConfig() { 41 | this(false); 42 | } 43 | 44 | public SDLinkRelayConfig(boolean wasReload) { 45 | super("sdlink", "simple-discord-link", "simple-discord-relay"); 46 | SDLinkRelayConfig.wasReload = wasReload; 47 | registerAndSetup(this); 48 | } 49 | 50 | @Override 51 | public void registerAndSetup(SDLinkRelayConfig config) { 52 | if (this.getConfigPath().exists() && this.getConfigPath().length() >= 2L) { 53 | this.migrateConfig(config); 54 | } else { 55 | this.saveConfig(config); 56 | } 57 | 58 | if (!wasReload) { 59 | ConfigController.register_config(this); 60 | } 61 | 62 | this.configReloaded(); 63 | } 64 | 65 | @Override 66 | public void migrateConfig(SDLinkRelayConfig conf) { 67 | CommentedFileConfig config = CommentedFileConfig.builder(getConfigPath()).sync().build(); 68 | CommentedFileConfig newConfig = CommentedFileConfig.builder(getConfigPath()).sync().build(); 69 | config.load(); 70 | 71 | if (config.getInt("configVersion") == configVer) { 72 | newConfig.close(); 73 | config.close(); 74 | return; 75 | } 76 | 77 | new ObjectConverter().toConfig(conf, newConfig); 78 | ((TomlConfigFormat)this.getConfigFormat()).updateConfigValues(config, newConfig, newConfig, ""); 79 | newConfig.set("configVersion", configVer); 80 | 81 | try { 82 | FileUtils.copyFile(getConfigPath(), new File(getConfigPath().getAbsolutePath().replace(".toml", ".old"))); 83 | } catch (IOException e) { 84 | BotController.INSTANCE.getLogger().warn("Failed to create config backup.", e); 85 | } 86 | 87 | newConfig.save(); 88 | newConfig.close(); 89 | config.close(); 90 | } 91 | 92 | @Override 93 | public void configReloaded() { 94 | INSTANCE = readConfig(this); 95 | hasConfigLoaded = true; 96 | SDLinkRelayClient.INSTANCE.openConnection(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/TriBoolean.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config; 2 | 3 | public enum TriBoolean { 4 | ALWAYS, 5 | NEVER, 6 | GAMERULE; 7 | 8 | public boolean isTrue() { 9 | return this == ALWAYS || this == GAMERULE; 10 | } 11 | 12 | public boolean isFalse() { 13 | return this == NEVER; 14 | } 15 | 16 | public boolean followGameRule() { 17 | return this == GAMERULE; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/AccessControl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.config.impl; 6 | 7 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 8 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public final class AccessControl { 14 | 15 | @Path("enabled") 16 | @SpecComment("Enable Access Control") 17 | public boolean enabled = false; 18 | 19 | @Path("optionalVerification") 20 | @SpecComment("Allow users to verify their accounts without access control. This setting is ignored if the above setting is set to true") 21 | public boolean optionalVerification = false; 22 | 23 | @Path("requireDiscordMembership") 24 | @SpecComment("Does the player need to be a member of your discord to join") 25 | public boolean requireDiscordMembership = false; 26 | 27 | @Path("allowMultipleAccounts") 28 | @SpecComment("Can players verify multiple Minecraft Accounts") 29 | public boolean allowMultipleAccounts = false; 30 | 31 | @Path("changeDiscordNickname") 32 | @SpecComment("Change the discord user nickname to their Minecraft name when their accounts are linked") 33 | public boolean changeDiscordNickname = false; 34 | 35 | @Path("requiredRoles") 36 | @SpecComment("Optional: The player requires any of these roles to be able to join your server") 37 | public List requiredRoles = new ArrayList<>(); 38 | 39 | @Path("deniedRoles") 40 | @SpecComment("Optional: Players with these roles will never be allowed access to your server") 41 | public List deniedRoles = new ArrayList<>(); 42 | 43 | @Path("verifiedRole") 44 | @SpecComment("Optional: Role name or ID to assign to verified player accounts") 45 | public List verifiedRole = new ArrayList<>(); 46 | 47 | @Path("verificationMessages") 48 | @SpecComment("Configure messages shown to players when they don't meet verification requirements") 49 | public AccessMessages verificationMessages = new AccessMessages(); 50 | 51 | @Path("banPlayerOnDiscordBan") 52 | @SpecComment("Should players with verified accounts, be banned from Minecraft if they get banned on discord") 53 | public boolean banPlayerOnDiscordBan = false; 54 | 55 | @Path("banMemberOnMinecraftBan") 56 | @SpecComment("Should members with verified accounts, be banned from discord when they are banned on Minecraft") 57 | public boolean banMemberOnMinecraftBan = false; 58 | 59 | public static class AccessMessages { 60 | 61 | @Path("optionalVerificationMessage") 62 | @SpecComment("This message is shown to users when they use the in-game verification command") 63 | public String optionalVerificationMessage = "Your verification code is: {code}. Please DM our bot, or use the /verify command in our discord to verify your account"; 64 | 65 | @Path("accountVerification") 66 | @SpecComment("The message shown to players that are not verified") 67 | public String accountVerify = "This server requires account verification. Your verification code is: {code}. Please visit our discord server for instructions on how to verify your account."; 68 | 69 | @Path("nonMember") 70 | @SpecComment("Message to show to players that are not a member of your discord") 71 | public String nonMember = "Sorry, you need to be a member of our discord server to join this server"; 72 | 73 | @Path("requireRoles") 74 | @SpecComment("Message to show when player doesn't have one of the required roles. Use {roles} to display the names of configured roles") 75 | public String requireRoles = "Sorry, but you require any of the following roles: {roles}"; 76 | 77 | @Path("roleDenied") 78 | @SpecComment("Message to show when player has a role from the deniedRoles list") 79 | public String roleDenied = "Sorry, but you are not allowed to access this server."; 80 | 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/BotConfigSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.config.impl; 6 | 7 | import net.dv8tion.jda.api.entities.Activity; 8 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 9 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 10 | import shadow.hypherionmc.moonconfig.core.fields.RandomArrayList; 11 | 12 | /** 13 | * @author HypherionSA 14 | * Config Structure for the Core bot settings 15 | */ 16 | public final class BotConfigSettings { 17 | 18 | @Path("botToken") 19 | @SpecComment("The token of the Discord Bot to use. This will be encrypted on first load. See https://sdlink.fdd-docs.com/installation/bot-creation/ to find this") 20 | public String botToken = ""; 21 | 22 | @Path("printInviteLink") 23 | @SpecComment("Print the bot invite link to the console on startup") 24 | public boolean printInviteLink = true; 25 | 26 | @Path("silentReplies") 27 | @SpecComment("Use silent replies when Slash Commands are used") 28 | public boolean silentReplies = true; 29 | 30 | @Path("statusUpdateInterval") 31 | @SpecComment("How often the Bot Status will update on Discord (in Seconds). Set to 0 to disable") 32 | public int statusUpdateInterval = 30; 33 | 34 | @Path("botStatus") 35 | @SpecComment("Control what the Discord Bot will display as it's status message") 36 | public RandomArrayList botStatus = RandomArrayList.of(new BotStatus()); 37 | 38 | @Path("topicUpdates") 39 | @SpecComment("Define how the bot should handle channel topic updates on the chat channel") 40 | public ChannelTopic channelTopic = new ChannelTopic(); 41 | 42 | @Path("invite") 43 | @SpecComment("Configure the in-game Discord Invite command") 44 | public DiscordInvite invite = new DiscordInvite(); 45 | 46 | public static class BotStatus { 47 | @Path("status") 48 | @SpecComment("Do not add Playing. A status to display on the bot. You can use %players% and %maxplayers% to show the number of players on the server") 49 | public String botStatus = "Enjoying Minecraft with %players%/%maxplayers% players"; 50 | 51 | @Path("botStatusType") 52 | @SpecComment("The type of the status displayed on the bot. Valid entries are: PLAYING, STREAMING, WATCHING, LISTENING, CUSTOM_STATUS") 53 | public Activity.ActivityType botStatusType = Activity.ActivityType.CUSTOM_STATUS; 54 | 55 | @Path("botStatusStreamingURL") 56 | @SpecComment("The URL that will be used when the \"botStatusType\" is set to \"STREAMING\", required to display as \"streaming\".") 57 | public String botStatusStreamingURL = "https://twitch.tv/twitch"; 58 | } 59 | 60 | public static class ChannelTopic { 61 | @Path("doTopicUpdates") 62 | @SpecComment("Should the bot update the topic of your chat channel automatically every 6 Minutes") 63 | public boolean doTopicUpdates = true; 64 | 65 | @Path("updateInterval") 66 | @SpecComment("How often should the bot update the channel topic (IN MINUTES)? CANNOT BE LOWER THAN 6 MINUTES!") 67 | public int updateInterval = 6; 68 | 69 | @Path("channelTopic") 70 | @SpecComment("A topic for the Chat Relay channel. You can use %player%, %maxplayers%, %uptime% or just leave it empty.") 71 | public String channelTopic = "Playing Minecraft with %players%/%maxplayers% people | Uptime: %uptime%"; 72 | } 73 | 74 | public static class DiscordInvite { 75 | @Path("inviteLink") 76 | @SpecComment("If this is defined, it will enable the in-game Discord command") 77 | public String inviteLink = ""; 78 | 79 | @Path("inviteMessage") 80 | @SpecComment("The message to show when someone uses /discord command. You can use %inviteurl%") 81 | public String inviteMessage = "Hey, check out our discord server here -> %inviteurl%"; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/ChannelWebhookConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.config.impl; 6 | 7 | 8 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 9 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 10 | 11 | /** 12 | * @author HypherionSA 13 | * Config Structure to control Channels and Webhooks used by the bot 14 | */ 15 | public final class ChannelWebhookConfig { 16 | 17 | @Path("serverAvatar") 18 | @SpecComment("A DIRECT link to an image to use as the avatar for server messages. Also used for embeds") 19 | public String serverAvatar = ""; 20 | 21 | @Path("serverName") 22 | @SpecComment("The name to display for Server messages when using Webhooks") 23 | public String serverName = "Minecraft Server"; 24 | 25 | @Path("channels") 26 | @SpecComment("Config relating to the discord channels to use with the mod") 27 | public Channels channels = new Channels(); 28 | 29 | @Path("webhooks") 30 | @SpecComment("Config relating to the discord Webhooks to use with the mod") 31 | public Webhooks webhooks = new Webhooks(); 32 | 33 | public static class Channels { 34 | @Path("chatChannelID") 35 | @SpecComment("REQUIRED! The ID of the channel to post in and relay messages from. This is still needed, even in webhook mode") 36 | public String chatChannelID = "0"; 37 | 38 | @Path("eventsChannelID") 39 | @SpecComment("If this ID is set, event messages will be posted in this channel instead of the chat channel") 40 | public String eventsChannelID = "0"; 41 | 42 | @Path("consoleChannelID") 43 | @SpecComment("If this ID is set, console messages sent after the bot started will be relayed here") 44 | public String consoleChannelID = "0"; 45 | } 46 | 47 | public static class Webhooks { 48 | @Path("enabled") 49 | @SpecComment("Prefer Webhook Messages over Standard Bot Messages") 50 | public boolean enabled = false; 51 | 52 | @Path("webhookNameFormat") 53 | @SpecComment("Change how the webhook name is displayed in discord. Available placeholders: %display_name%, %mc_name%") 54 | public String webhookNameFormat = "%display_name%"; 55 | 56 | @Path("useServerForChat") 57 | @SpecComment("Use Server Author for chat messages, instead of the real author information") 58 | public boolean useServerForChat = false; 59 | 60 | @Path("chatWebhook") 61 | @SpecComment("The URL of the channel webhook to use for Chat Messages. Will be encrypted on first run") 62 | public String chatWebhook = ""; 63 | 64 | @Path("eventsWebhook") 65 | @SpecComment("The URL of the channel webhook to use for Server Messages. Will be encrypted on first run") 66 | public String eventsWebhook = ""; 67 | 68 | @Path("consoleWebhook") 69 | @SpecComment("The URL of the channel webhook to use for Console Messages. DOES NOT WORK FOR CONSOLE RELAY! Will be encrypted on first run") 70 | public String consoleWebhook = ""; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/GeneralConfigSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.config.impl; 6 | 7 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 8 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 9 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 10 | 11 | /** 12 | * @author HypherionSA 13 | * General Mod Settings config Structure 14 | */ 15 | public final class GeneralConfigSettings { 16 | 17 | @Path("enabled") 18 | @SpecComment("Should the mod be enabled or not") 19 | public boolean enabled = true; 20 | 21 | @Path("debugging") 22 | @SpecComment("Enable Additional Logging. Used for Fault Finding. WARNING: CAUSES LOG SPAM!") 23 | public boolean debugging = false; 24 | 25 | @Path("language") 26 | @SpecComment("The active language to use for built in messages. Defaults to en_us if a language is not found") 27 | public String language = "en_us"; 28 | 29 | @Path("configVersion") 30 | @SpecComment("Internal version control. DO NOT TOUCH!") 31 | public int configVersion = SDLinkConfig.configVer; 32 | } 33 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/MessageChannelConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.config.impl; 6 | 7 | import com.hypherionmc.sdlink.api.messaging.MessageDestination; 8 | import com.hypherionmc.sdlink.api.messaging.MessageType; 9 | import com.hypherionmc.sdlink.util.DestinationHolder; 10 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 11 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 12 | 13 | /** 14 | * @author HypherionSA 15 | * Config Structure to control the destinations of messages 16 | */ 17 | public final class MessageChannelConfig { 18 | 19 | @Path("chat") 20 | @SpecComment("Control where CHAT messages are delivered") 21 | public DestinationObject chat = DestinationObject.of(MessageDestination.CHAT, false, "default"); 22 | 23 | @Path("start") 24 | @SpecComment("Control where START messages are delivered") 25 | public DestinationObject start = DestinationObject.of(MessageDestination.EVENT, false, "default"); 26 | 27 | @Path("stop") 28 | @SpecComment("Control where STOP messages are delivered") 29 | public DestinationObject stop = DestinationObject.of(MessageDestination.EVENT, false, "default"); 30 | 31 | @Path("join") 32 | @SpecComment("Control where JOIN messages are delivered") 33 | public DestinationObject join = DestinationObject.of(MessageDestination.EVENT, false, "default"); 34 | 35 | @Path("leave") 36 | @SpecComment("Control where LEAVE messages are delivered") 37 | public DestinationObject leave = DestinationObject.of(MessageDestination.EVENT, false, "default"); 38 | 39 | @Path("advancements") 40 | @SpecComment("Control where ADVANCEMENT messages are delivered") 41 | public DestinationObject advancements = DestinationObject.of(MessageDestination.EVENT, false, "default"); 42 | 43 | @Path("death") 44 | @SpecComment("Control where DEATH messages are delivered") 45 | public DestinationObject death = DestinationObject.of(MessageDestination.EVENT, false, "default"); 46 | 47 | @Path("commands") 48 | @SpecComment("Control where COMMAND messages are delivered") 49 | public DestinationObject commands = DestinationObject.of(MessageDestination.EVENT, false, "default"); 50 | 51 | @Path("whitelist") 52 | @SpecComment("Control where WHITELIST change messages are delivered") 53 | public DestinationObject whitelist = DestinationObject.of(MessageDestination.CONSOLE, false, "default"); 54 | 55 | @Path("custom") 56 | @SpecComment("Control where messages that match none of the above are delivered") 57 | public DestinationObject custom = DestinationObject.of(MessageDestination.EVENT, false, "default"); 58 | 59 | public static class DestinationObject { 60 | @Path("channel") 61 | @SpecComment("The Channel the message will be delivered to. Valid entries are CHAT, EVENT, CONSOLE, OVERRIDE") 62 | public MessageDestination channel; 63 | 64 | @Path("useEmbed") 65 | @SpecComment("Should the message be sent using EMBED style messages") 66 | public boolean useEmbed; 67 | 68 | @Path("embedLayout") 69 | @SpecComment("Embed Layout to use") 70 | public String embedLayout; 71 | 72 | @Path("override") 73 | @SpecComment("Override the destination with a custom channel/webhook url. Make sure to change `channel` above to OVERRIDE") 74 | public String override; 75 | 76 | DestinationObject(MessageDestination destination, boolean useEmbed, String embedLayout, String override) { 77 | this.channel = destination; 78 | this.useEmbed = useEmbed; 79 | this.embedLayout = embedLayout; 80 | this.override = override; 81 | } 82 | 83 | public static DestinationObject of(MessageDestination destination, boolean useEmbed, String embedLayout) { 84 | return new DestinationObject(destination, useEmbed, embedLayout, ""); 85 | } 86 | 87 | public DestinationHolder toHolder(MessageType type) { 88 | return DestinationHolder.of(this, type); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/MessageFormatting.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.config.impl; 6 | 7 | 8 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 9 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 10 | 11 | /** 12 | * @author HypherionSA 13 | * Config Structure to control Discord/MC Message Formatting 14 | */ 15 | public final class MessageFormatting { 16 | 17 | @Path("mcPrefix") 18 | @SpecComment("Prefix to add to Minecraft when a message is relayed from Discord. Supports MiniMessage formatting. Use %user% for the Discord Username") 19 | public String mcPrefix = "[Discord] %user%: "; 20 | 21 | @Path("mcReplyFormatting") 22 | @SpecComment("How messages relayed from discord that are replies to other messages are formatted. Supports MiniMessage formatting") 23 | public String mcReplyFormatting = " ┌──── %color%@%replier_name%%end_color% %message_summary%"; 24 | 25 | @Path("serverStarting") 26 | @SpecComment("Server Starting Message") 27 | public String serverStarting = "*Server is starting...*"; 28 | 29 | @Path("serverStarted") 30 | @SpecComment("Server Started Message") 31 | public String serverStarted = "*Server has started. Enjoy!*"; 32 | 33 | @Path("serverStopping") 34 | @SpecComment("Server Stopping Message") 35 | public String serverStopping = "*Server is stopping...*"; 36 | 37 | @Path("serverStopped") 38 | @SpecComment("Server Stopped Message") 39 | public String serverStopped = "*Server has stopped...*"; 40 | 41 | @Path("playerJoined") 42 | @SpecComment("Player Joined Message. Use %player% to display the player name") 43 | public String playerJoined = "*%player% has joined the server!*"; 44 | 45 | @Path("playerLeft") 46 | @SpecComment("Player Left Message. Use %player% to display the player name") 47 | public String playerLeft = "*%player% has left the server!*"; 48 | 49 | @Path("advancements") 50 | @SpecComment("Advancement Messages. Available variables: %player%, %title%, %description%") 51 | public String achievements = "*%player% has made the advancement [%title%]: %description%*"; 52 | 53 | @Path("chat") 54 | @SpecComment("Chat Messages. THIS DOES NOT APPLY TO EMBED OR WEBHOOK MESSAGES. Available variables: %player%, %message%, %mcname%") 55 | public String chat = "%player%: %message%"; 56 | 57 | @Path("death") 58 | @SpecComment("Death Messages. Available variables: %player%, %message%") 59 | public String death = "%player% %message%"; 60 | 61 | @Path("whitelistAdded") 62 | @SpecComment("Message to be sent when a player is added to the whitelist") 63 | public String whitelistAdded = "%player% has been whitelisted!"; 64 | 65 | @Path("whitelistRemoved") 66 | @SpecComment("Message to be sent when a player is removed from the whitelist") 67 | public String whitelistRemoved = "%player% has been removed from the whitelist!"; 68 | 69 | @Path("commands") 70 | @SpecComment("Command Messages. Available variables: %player%, %command%") 71 | public String commands = "%player% **executed command**: *%command%*"; 72 | } 73 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/MessageIgnoreConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.config.impl; 6 | 7 | import com.hypherionmc.sdlink.core.config.AppliesTo; 8 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 9 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public final class MessageIgnoreConfig { 15 | 16 | @Path("enabled") 17 | @SpecComment("Enable the filter system") 18 | public boolean enabled = true; 19 | 20 | @Path("entries") 21 | @SpecComment("List of entries to process") 22 | public List entries = new ArrayList<>(); 23 | 24 | @Path("ignoredThreads") 25 | @SpecComment("Ignore messages sent from certain threads. Enable debug logging to see what thread the message is from") 26 | public List ignoredThread = new ArrayList<>(); 27 | 28 | public enum FilterMode { 29 | STARTS_WITH, 30 | MATCHES, 31 | CONTAINS, 32 | REGEX 33 | } 34 | 35 | public enum ActionMode { 36 | REPLACE, 37 | IGNORE 38 | } 39 | 40 | public enum FilterTarget { 41 | CHAT, 42 | USERNAME, 43 | BOTH, 44 | CONSOLE; 45 | 46 | public boolean isChat() { 47 | return this == FilterTarget.CHAT || this == FilterTarget.BOTH; 48 | } 49 | 50 | public boolean isUsername() { 51 | return this == FilterTarget.USERNAME || this == FilterTarget.BOTH; 52 | } 53 | 54 | public boolean isConsole() { 55 | return this == FilterTarget.CONSOLE || this == FilterTarget.BOTH; 56 | } 57 | } 58 | 59 | public static class Ignore { 60 | @Path("target") 61 | @SpecComment("Should this filter target CHAT, USERNAME, BOTH, or CONSOLE.") 62 | public FilterTarget target = FilterTarget.CHAT; 63 | 64 | @Path("ignoreConsole") 65 | @SpecComment("Ignore this filter in Console Messages") 66 | public boolean ignoreConsole = false; 67 | 68 | @Path("appliesTo") 69 | @SpecComment("What way of relay does this filter apply to. DISCORD or MINECRAFT") 70 | public AppliesTo appliesTo = AppliesTo.DISCORD; 71 | 72 | @Path("search") 73 | @SpecComment("The text to search for in the message") 74 | public String search; 75 | 76 | @Path("replace") 77 | @SpecComment("Text to replace `search` with, if it's found. Leave empty to ignore") 78 | public String replace; 79 | 80 | @Path("searchMode") 81 | @SpecComment("How should `search` be found in the text. Valid entries are STARTS_WITH, MATCHES and CONTAINS, REGEX") 82 | public FilterMode searchMode = FilterMode.CONTAINS; 83 | 84 | @Path("action") 85 | @SpecComment("How should `replace` be treated, when `search` is found using `searchMode`") 86 | public ActionMode action = ActionMode.REPLACE; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/MinecraftCommands.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.config.impl; 6 | 7 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 8 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public final class MinecraftCommands { 14 | 15 | @Path("enabled") 16 | @SpecComment("Allow executing Minecraft commands from Discord") 17 | public boolean enabled = false; 18 | 19 | @Path("prefix") 20 | @SpecComment("Command Prefix. For example ?weather clear") 21 | public String prefix = "?"; 22 | 23 | @Path("keepReplies") 24 | @SpecComment("Should command replies be deleted automatically or not") 25 | public boolean keepReplies = false; 26 | 27 | @Path("keepOriginal") 28 | @SpecComment("Should the original message that was sent to trigger the command be deleted automatically or not") 29 | public boolean keepOriginal = false; 30 | 31 | @Path("outputInvalid") 32 | @SpecComment("Should error messages be sent for invalid, or disallowed commands") 33 | public boolean outputInvalid = true; 34 | 35 | @Path("allowedChannels") 36 | @SpecComment("You can leave this empty, or enter the channel ID's (surrounded by \"\") of channels where linked commands can be used") 37 | public List allowedChannels = new ArrayList<>(); 38 | 39 | @Path("permissions") 40 | @SpecComment("List of command permissions") 41 | public List permissions = new ArrayList<>(); 42 | 43 | public static class Command { 44 | public String role = "0"; 45 | 46 | public List commands = new ArrayList<>(); 47 | 48 | public int permissionLevel = 1; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/RelayMessageConfig.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config.impl; 2 | 3 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 4 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 5 | 6 | public final class RelayMessageConfig { 7 | 8 | @Path("relayMinecraftChats") 9 | @SpecComment("Should Minecraft Chats be relayed to all connected servers") 10 | public boolean relayMinecraftChats = true; 11 | 12 | @Path("relayDiscordChats") 13 | @SpecComment("Should Discord Chat Messages be relayed to all connected servers") 14 | public boolean relayDiscordChats = true; 15 | 16 | @Path("relayMinecraftToDiscord") 17 | @SpecComment("Should Minecraft Messages be relayed to connected discord servers") 18 | public boolean relayMinecraftToDiscord = true; 19 | 20 | @Path("relayDeathMessages") 21 | @SpecComment("Should player death messages be relayed to all connected servers") 22 | public boolean relayDeathMessages = true; 23 | 24 | @Path("relayAdvancementMessages") 25 | @SpecComment("Should player advancement messages be relayed to all connected servers") 26 | public boolean relayAdvancementMessages = true; 27 | 28 | @Path("relayJoinMessages") 29 | @SpecComment("Should player join messages be relayed to all connected servers") 30 | public boolean relayJoinMessages = true; 31 | 32 | @Path("relayLeaveMessages") 33 | @SpecComment("Should player leave messages be relayed to all connected servers") 34 | public boolean relayLeaveMessages = true; 35 | 36 | @Path("relayMessagePrefix") 37 | @SpecComment("The Prefix to use for messages relayed to other servers. Set this to empty to ignore it") 38 | public String relayMessagePrefix = "[%server_name%]"; 39 | } 40 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/RelayServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config.impl; 2 | 3 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 4 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 5 | 6 | public final class RelayServerConfig { 7 | 8 | @Path("enabled") 9 | @SpecComment("Enable or Disable the relay server") 10 | public boolean enabled = false; 11 | 12 | @Path("relayServerUrl") 13 | @SpecComment("The Relay Server to connect to. If you use an IP address, include the port, like 127.0.0.1:1234") 14 | public String relayServerUrl = "sdlinkrelay.firstdark.dev"; 15 | 16 | @Path("relayToken") 17 | @SpecComment("A secret, password if you will, to connect your servers with. This has to match on all connected servers") 18 | public String relayToken = ""; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/TriggerCommandsConfig.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config.impl; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.NoArgsConstructor; 5 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 6 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public final class TriggerCommandsConfig { 12 | 13 | @Path("enabled") 14 | @SpecComment("Should any of the below commands be executed when a role changes") 15 | public boolean enabled = false; 16 | 17 | @Path("roleAdded") 18 | @SpecComment("Commands to run when roles are added") 19 | public List roleAdded = new ArrayList<>(); 20 | 21 | @Path("roleRemoved") 22 | @SpecComment("Commands to run when roles are removed") 23 | public List roleRemoved = new ArrayList<>(); 24 | 25 | @NoArgsConstructor 26 | @AllArgsConstructor(staticName = "of") 27 | public static class TriggerHolder { 28 | 29 | @Path("discordRole") 30 | public String discordRole = ""; 31 | 32 | @Path("minecraftCommand") 33 | public List minecraftCommand = new ArrayList<>(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/compat/CommonCompat.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config.impl.compat; 2 | 3 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 4 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 5 | 6 | public final class CommonCompat { 7 | 8 | @Path("vanish") 9 | @SpecComment("Should SDLink integrate with Vanish Mod") 10 | public boolean vanish = true; 11 | 12 | @Path("ftbessentials") 13 | @SpecComment("Should SDLink integrate with FTB Essentials") 14 | public boolean ftbessentials = true; 15 | 16 | @Path("ftbranks") 17 | @SpecComment("Should SDLink integrate with FTB Ranks") 18 | public boolean ftbranks = true; 19 | 20 | @Path("luckperms") 21 | @SpecComment("Should SDLink integrate with Luckperms (Group Syncing only)") 22 | public boolean luckperms = true; 23 | 24 | @Path("playerroles") 25 | @SpecComment("Should SDLink integrate with Player Roles") 26 | public boolean playerroles = true; 27 | 28 | @Path("cobblemonguilds") 29 | @SpecComment("Should SDLink integrate with Cobblemon Guilds") 30 | public boolean cobblemonguilds = true; 31 | 32 | @Path("ftbteams_chat") 33 | @SpecComment("Should chats from the /ftbteams chat command be relayed to discord") 34 | public boolean ftbteams_chat = false; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/compat/MaintenanceModeCompat.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config.impl.compat; 2 | 3 | import net.dv8tion.jda.api.OnlineStatus; 4 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 5 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 6 | 7 | public final class MaintenanceModeCompat { 8 | 9 | @Path("enabled") 10 | @SpecComment("Should integration with MaintenanceMode be enabled") 11 | public boolean enabled = true; 12 | 13 | @Path("maintenanceOnlineStatus") 14 | @SpecComment("Change the Bot Online Status during Maintenance Mode. Valid options are ONLINE, IDLE, DO_NOT_DISTURB, OFFLINE") 15 | public OnlineStatus onlineStatus = OnlineStatus.DO_NOT_DISTURB; 16 | 17 | @Path("updateChannelTopic") 18 | @SpecComment("Update channel topic with server MOTD during maintenance mode") 19 | public boolean updateChannelTopic = true; 20 | 21 | @Path("updateBotStatus") 22 | @SpecComment("Update the bot status with the server MOTD during maintenance mode") 23 | public boolean updateBotStatus = false; 24 | 25 | @Path("sendMaintenanceStart") 26 | @SpecComment("Send a message to discord when maintenance starts") 27 | public boolean sendMaintenanceStart = true; 28 | 29 | @Path("sendMaintenanceEnd") 30 | @SpecComment("Send a message to discord when maintenance ends") 31 | public boolean sendMaintenanceEnd = true; 32 | 33 | @Path("maintenanceStartMessage") 34 | @SpecComment("The message to send to discord when maintenance has started") 35 | public String maintenanceStartMessage = "Maintenance has started"; 36 | 37 | @Path("maintenanceEndMessage") 38 | @SpecComment("The message to send to discord when maintenance has ended") 39 | public String maintenanceEndMessage = "Maintenance has ended"; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/compat/PlayerReviveCompat.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config.impl.compat; 2 | 3 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 4 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 5 | 6 | public final class PlayerReviveCompat { 7 | 8 | @Path("enabled") 9 | @SpecComment("Should integration with Player Revive Mod be enabled") 10 | public boolean enabled = true; 11 | 12 | @Path("reviveWaitingMessage") 13 | @SpecComment("Message to be sent to discord, while the player is waiting to be revived") 14 | public String reviveWaitingMessage = "%player% is bleeding out and may need your help"; 15 | 16 | @Path("revivedMessage") 17 | @SpecComment("Message to be sent to discord, when the player is revived") 18 | public String revivedMessage = "%player% has been revived"; 19 | 20 | @Path("playerBledOutMessage") 21 | @SpecComment("Message to be sent to discord, when the player dies for real") 22 | public String playerBledOutMessage = "%player% %message%"; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/compat/RoleSyncCompat.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config.impl.compat; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.NoArgsConstructor; 5 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 6 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public final class RoleSyncCompat { 12 | 13 | @Path("syncToMinecraft") 14 | @SpecComment("Sync Groups/Ranks to Minecraft from Discord Roles") 15 | public boolean syncToMinecraft = false; 16 | 17 | @Path("syncToDiscord") 18 | @SpecComment("Sync Groups/Ranks to Discord roles from Minecraft") 19 | public boolean syncToDiscord = false; 20 | 21 | @Path("syncs") 22 | @SpecComment("List of Ranks and Roles that will be synced. Check the wiki on how to configure this") 23 | public List syncs = new ArrayList<>(); 24 | 25 | @AllArgsConstructor(staticName = "of") 26 | @NoArgsConstructor 27 | public static class Sync { 28 | public String rank; 29 | public String role; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/config/impl/compat/VanishCompat.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.config.impl.compat; 2 | 3 | import shadow.hypherionmc.moonconfig.core.conversion.Path; 4 | import shadow.hypherionmc.moonconfig.core.conversion.SpecComment; 5 | 6 | public final class VanishCompat { 7 | 8 | @Path("sendFakeJoinLeaveMessage") 9 | @SpecComment("Should Fake Join/Leave message be sent when players vanish/unvanish") 10 | public boolean sendFakeJoinLeaveMessage = true; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/database/HiddenPlayers.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.database; 2 | 3 | import com.hypherionmc.sdlink.core.jsondb.annotations.Document; 4 | import com.hypherionmc.sdlink.core.jsondb.annotations.Id; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | 10 | @Setter 11 | @Getter 12 | @AllArgsConstructor(staticName = "of") 13 | @NoArgsConstructor 14 | @Document(collection = "hiddenplayers") 15 | public final class HiddenPlayers { 16 | 17 | @Id 18 | private String identifier; 19 | private String displayName; 20 | private String type; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/database/SDLinkAccount.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.database; 6 | 7 | import com.hypherionmc.sdlink.core.jsondb.annotations.Document; 8 | import com.hypherionmc.sdlink.core.jsondb.annotations.Id; 9 | import lombok.Getter; 10 | import lombok.Setter; 11 | 12 | @Setter 13 | @Getter 14 | @Document(collection = "verifiedaccounts") 15 | public final class SDLinkAccount { 16 | 17 | @Id 18 | private String uuid; 19 | private String username; 20 | private String inGameName; 21 | private String discordID; 22 | private String verifyCode; 23 | private boolean isOffline; 24 | 25 | public String getInGameName() { 26 | return inGameName == null || inGameName.isEmpty() ? username : inGameName; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/CommandManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.discord.commands; 6 | 7 | import com.hypherionmc.craterlib.core.event.CraterEventBus; 8 | import com.hypherionmc.sdlink.api.events.SlashCommandRegistrationEvent; 9 | import com.hypherionmc.sdlink.core.discord.commands.slash.general.HelpSlashCommand; 10 | import com.hypherionmc.sdlink.core.discord.commands.slash.general.PlayerListSlashCommand; 11 | import com.hypherionmc.sdlink.core.discord.commands.slash.general.ServerStatusSlashCommand; 12 | import com.hypherionmc.sdlink.core.discord.commands.slash.hide.HiddenPlayersCommand; 13 | import com.hypherionmc.sdlink.core.discord.commands.slash.hide.HidePlayerCommand; 14 | import com.hypherionmc.sdlink.core.discord.commands.slash.hide.UnhidePlayerCommand; 15 | import com.hypherionmc.sdlink.core.discord.commands.slash.setup.ReloadCacheCommand; 16 | import com.hypherionmc.sdlink.core.discord.commands.slash.setup.SetChannelCommand; 17 | import com.hypherionmc.sdlink.core.discord.commands.slash.verification.*; 18 | import com.jagrosh.jdautilities.command.CommandClient; 19 | import com.jagrosh.jdautilities.command.SlashCommand; 20 | import lombok.Getter; 21 | 22 | import java.util.HashSet; 23 | import java.util.Set; 24 | 25 | /** 26 | * @author HypherionSA 27 | * Command Manager class to control how commands are registered to discord 28 | */ 29 | @Getter 30 | public final class CommandManager { 31 | 32 | public static final CommandManager INSTANCE = new CommandManager(); 33 | 34 | private final Set commands = new HashSet<>(); 35 | 36 | private CommandManager() { 37 | this.addCommands(); 38 | } 39 | 40 | private void addCommands() { 41 | commands.clear(); 42 | 43 | // Access Control Commands 44 | commands.add(new VerifyAccountCommand()); 45 | commands.add(new UnverifyAccountSlashCommand()); 46 | commands.add(new StaffUnverifyCommand()); 47 | commands.add(new StaffVerifyAccountCommand()); 48 | commands.add(new ViewVerifiedAccounts()); 49 | 50 | // Enable the Server Status command 51 | commands.add(new ServerStatusSlashCommand()); 52 | 53 | // Enable the Player List command 54 | commands.add(new PlayerListSlashCommand()); 55 | 56 | // Enable the Help command 57 | commands.add(new HelpSlashCommand()); 58 | 59 | // SetChannel config Command 60 | commands.add(new SetChannelCommand()); 61 | 62 | // Reload Cache command 63 | commands.add(new ReloadCacheCommand()); 64 | 65 | // Hidden Players 66 | commands.add(new HidePlayerCommand()); 67 | commands.add(new HiddenPlayersCommand()); 68 | commands.add(new UnhidePlayerCommand()); 69 | } 70 | 71 | /** 72 | * INTERNAL. Used to register slash commands 73 | * 74 | * @param client The Discord Command Client instance 75 | */ 76 | public void register(CommandClient client) { 77 | SlashCommandRegistrationEvent event = new SlashCommandRegistrationEvent(); 78 | CraterEventBus.INSTANCE.postEvent(event); 79 | commands.addAll(event.getCommands()); 80 | 81 | commands.forEach(client::addSlashCommand); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/SDLinkSlashCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.discord.commands.slash; 6 | 7 | import com.jagrosh.jdautilities.command.SlashCommand; 8 | import net.dv8tion.jda.api.Permission; 9 | 10 | /** 11 | * @author HypherionSA 12 | * Extention of {@link SlashCommand} to implement our Permission handling 13 | */ 14 | public abstract class SDLinkSlashCommand extends SlashCommand { 15 | 16 | public SDLinkSlashCommand(boolean requiresPerms) { 17 | this.guildOnly = true; 18 | 19 | if (requiresPerms) { 20 | this.userPermissions = new Permission[]{Permission.MANAGE_SERVER}; 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/general/HelpSlashCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.discord.commands.slash.general; 6 | 7 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 8 | import com.hypherionmc.sdlink.core.discord.commands.CommandManager; 9 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 10 | import com.hypherionmc.sdlink.util.translations.Text; 11 | import com.jagrosh.jdautilities.command.SlashCommand; 12 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 13 | import net.dv8tion.jda.api.EmbedBuilder; 14 | 15 | import java.awt.*; 16 | import java.util.Set; 17 | 18 | /** 19 | * @author HypherionSA 20 | * The Help Command for the bot 21 | */ 22 | public final class HelpSlashCommand extends SDLinkSlashCommand { 23 | 24 | public HelpSlashCommand() { 25 | super(false); 26 | this.name = "help"; 27 | this.help = Text.translate("command.help.help").toString(); 28 | } 29 | 30 | @Override 31 | protected void execute(SlashCommandEvent event) { 32 | event.deferReply(true).queue(); 33 | Set commands = CommandManager.INSTANCE.getCommands(); 34 | 35 | EmbedBuilder builder = new EmbedBuilder(); 36 | builder.setTitle(Text.translate("command.help.title").toString()); 37 | builder.setColor(Color.BLUE); 38 | 39 | commands.forEach(cmd -> builder.addField(cmd.getName(), cmd.getHelp(), false)); 40 | event.getHook().sendMessageEmbeds(builder.build()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/general/PlayerListSlashCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.discord.commands.slash.general; 6 | 7 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 8 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 9 | import com.hypherionmc.sdlink.core.discord.BotController; 10 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 11 | import com.hypherionmc.sdlink.core.services.SDLinkPlatform; 12 | import com.hypherionmc.sdlink.util.MessageUtil; 13 | import com.hypherionmc.sdlink.util.translations.Text; 14 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 15 | import com.jagrosh.jdautilities.menu.ButtonEmbedPaginator; 16 | import net.dv8tion.jda.api.EmbedBuilder; 17 | import net.dv8tion.jda.api.entities.MessageEmbed; 18 | 19 | import java.awt.*; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.concurrent.atomic.AtomicInteger; 23 | 24 | /** 25 | * @author HypherionSA 26 | * Command to view a list of online players currently on the server 27 | */ 28 | public final class PlayerListSlashCommand extends SDLinkSlashCommand { 29 | 30 | public PlayerListSlashCommand() { 31 | super(false); 32 | 33 | this.name = "playerlist"; 34 | this.help = Text.translate("command.playerlist.help").toString(); 35 | this.guildOnly = true; 36 | } 37 | 38 | @Override 39 | protected void execute(SlashCommandEvent event) { 40 | event.deferReply(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 41 | 42 | try { 43 | List players = SDLinkPlatform.minecraftHelper.getOnlinePlayers(); 44 | 45 | EmbedBuilder builder = new EmbedBuilder(); 46 | List pages = new ArrayList<>(); 47 | AtomicInteger count = new AtomicInteger(); 48 | 49 | if (players.isEmpty()) { 50 | builder.setTitle(Text.translate("command.playerlist.title").toString()); 51 | builder.setColor(Color.RED); 52 | builder.setDescription(Text.translate("command.playerlist.no_online")); 53 | event.getHook().sendMessageEmbeds(builder.build()).setEphemeral(true).queue(); 54 | return; 55 | } 56 | 57 | ButtonEmbedPaginator.Builder paginator = MessageUtil.defaultPaginator(); 58 | 59 | /** 60 | * Use Pagination to avoid message limits 61 | */ 62 | MessageUtil.listBatches(players, 10).forEach(p -> { 63 | StringBuilder sb = new StringBuilder(); 64 | count.getAndIncrement(); 65 | builder.clear(); 66 | builder.setTitle(Text.translate("command.playerlist.title_page", count.get(),(int) Math.ceil(((float) players.size() / 10))).toString()); 67 | builder.setColor(Color.GREEN); 68 | builder.setFooter(Text.translate("command.playerlist.footer", SDLinkPlatform.minecraftHelper.getPlayerCounts().getLeft(), SDLinkPlatform.minecraftHelper.getPlayerCounts().getRight()).toString()); 69 | 70 | p.forEach(account -> { 71 | sb.append("`").append(account.getUsername()).append("`"); 72 | 73 | if ((SDLinkConfig.INSTANCE.accessControl.enabled || SDLinkConfig.INSTANCE.accessControl.optionalVerification) && account.getDiscordUser() != null) { 74 | sb.append(" - ").append(account.getDiscordUser().getAsMention()); 75 | } 76 | sb.append("\r\n"); 77 | }); 78 | 79 | builder.setDescription(sb.toString()); 80 | pages.add(builder.build()); 81 | }); 82 | 83 | paginator.setItems(pages); 84 | ButtonEmbedPaginator embedPaginator = paginator.build(); 85 | 86 | event.getHook().sendMessageEmbeds(pages.get(0)).setEphemeral(false).queue(success -> embedPaginator.paginate(success, 1)); 87 | } catch (Exception e) { 88 | event.getHook().sendMessage(Text.translate("error.command_failed").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 89 | BotController.INSTANCE.getLogger().error("Failed to run player list command", e); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/general/ServerStatusSlashCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.discord.commands.slash.general; 6 | 7 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 8 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 9 | import com.hypherionmc.sdlink.core.services.SDLinkPlatform; 10 | import com.hypherionmc.sdlink.util.SystemUtils; 11 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 12 | import net.dv8tion.jda.api.EmbedBuilder; 13 | import net.dv8tion.jda.api.entities.MessageEmbed; 14 | import net.dv8tion.jda.api.interactions.components.buttons.Button; 15 | import oshi.SystemInfo; 16 | import oshi.hardware.CentralProcessor; 17 | import oshi.hardware.HardwareAbstractionLayer; 18 | 19 | /** 20 | * @author HypherionSA 21 | * Informational command to give you a quick overview of the hardware/player 22 | * status of your server 23 | */ 24 | // TODO: Translation 25 | public final class ServerStatusSlashCommand extends SDLinkSlashCommand { 26 | 27 | public ServerStatusSlashCommand() { 28 | super(true); 29 | 30 | this.name = "status"; 31 | this.help = "View information about your server"; 32 | this.guildOnly = true; 33 | } 34 | 35 | public static MessageEmbed runStatusCommand() { 36 | SystemInfo systemInfo = new SystemInfo(); 37 | HardwareAbstractionLayer hal = systemInfo.getHardware(); 38 | CentralProcessor cpu = hal.getProcessor(); 39 | 40 | EmbedBuilder builder = new EmbedBuilder(); 41 | builder.setTitle("Server Information / Status"); 42 | 43 | StringBuilder stringBuilder = new StringBuilder(); 44 | stringBuilder.append("**__System Information__**\r\n\r\n"); 45 | 46 | stringBuilder 47 | .append("**CPU:**\r\n```\r\n") 48 | .append(cpu.toString()) 49 | .append("```") 50 | .append("\r\n"); 51 | 52 | try { 53 | stringBuilder 54 | .append("**Memory:**\r\n```\r\n") 55 | .append(SystemUtils.byteToHuman(hal.getMemory().getAvailable())) 56 | .append(" free of ") 57 | .append(SystemUtils.byteToHuman(hal.getMemory().getTotal())) 58 | .append("```\r\n"); 59 | } catch (Exception ignored) {} 60 | 61 | stringBuilder 62 | .append("**OS:**\r\n```\r\n") 63 | .append(systemInfo.getOperatingSystem().toString()) 64 | .append(" (") 65 | .append(systemInfo.getOperatingSystem().getBitness()) 66 | .append(" bit)\r\n") 67 | .append("Version: ") 68 | .append(systemInfo.getOperatingSystem().getVersionInfo().getVersion()) 69 | .append("```\r\n"); 70 | 71 | stringBuilder 72 | .append("**System Uptime:**\r\n```\r\n") 73 | .append(SystemUtils.secondsToTimestamp(systemInfo.getOperatingSystem().getSystemUptime())) 74 | .append("```\r\n"); 75 | 76 | stringBuilder.append("**__Minecraft Information__**\r\n\r\n"); 77 | 78 | stringBuilder 79 | .append("**Server Uptime:**\r\n```\r\n") 80 | .append(SystemUtils.secondsToTimestamp(SDLinkPlatform.minecraftHelper.getServerUptime())) 81 | .append("```\r\n"); 82 | 83 | stringBuilder 84 | .append("**Server Version:**\r\n```\r\n") 85 | .append(SDLinkPlatform.minecraftHelper.getServerVersion()) 86 | .append("```\r\n"); 87 | 88 | stringBuilder 89 | .append("**Players Online:**\r\n```\r\n") 90 | .append(SDLinkPlatform.minecraftHelper.getPlayerCounts().getLeft()) 91 | .append("/") 92 | .append(SDLinkPlatform.minecraftHelper.getPlayerCounts().getRight()) 93 | .append("```\r\n"); 94 | 95 | stringBuilder 96 | .append("**Whitelisting:**\r\n```\r\n") 97 | .append(!SDLinkPlatform.minecraftHelper.checkWhitelisting().isError() ? "Enabled" : "Disabled") 98 | .append("```\r\n"); 99 | 100 | builder.setDescription(stringBuilder.toString()); 101 | 102 | return builder.build(); 103 | } 104 | 105 | @Override 106 | protected void execute(SlashCommandEvent event) { 107 | event.deferReply(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 108 | Button refreshBtn = Button.danger("sdrefreshbtn", "Refresh"); 109 | event.getHook().sendMessageEmbeds(runStatusCommand()).addActionRow(refreshBtn).queue(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/hide/HiddenPlayersCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.discord.commands.slash.hide; 2 | 3 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 4 | import com.hypherionmc.sdlink.core.database.HiddenPlayers; 5 | import com.hypherionmc.sdlink.core.discord.BotController; 6 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 7 | import com.hypherionmc.sdlink.core.managers.HiddenPlayersManager; 8 | import com.hypherionmc.sdlink.util.MessageUtil; 9 | import com.hypherionmc.sdlink.util.translations.Text; 10 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 11 | import com.jagrosh.jdautilities.menu.ButtonEmbedPaginator; 12 | import net.dv8tion.jda.api.EmbedBuilder; 13 | import net.dv8tion.jda.api.entities.MessageEmbed; 14 | 15 | import java.awt.*; 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.concurrent.atomic.AtomicInteger; 20 | 21 | public final class HiddenPlayersCommand extends SDLinkSlashCommand { 22 | 23 | public HiddenPlayersCommand() { 24 | super(true); 25 | 26 | this.name = "hiddenplayers"; 27 | this.help = Text.translate("command.hiddenplayers.help").toString(); 28 | this.guildOnly = true; 29 | } 30 | 31 | @Override 32 | protected void execute(SlashCommandEvent event) { 33 | event.deferReply(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 34 | 35 | try { 36 | HashMap hiddenPlayers = HiddenPlayersManager.INSTANCE.getHiddenPlayers(); 37 | 38 | EmbedBuilder builder = new EmbedBuilder(); 39 | List pages = new ArrayList<>(); 40 | AtomicInteger count = new AtomicInteger(); 41 | 42 | if (hiddenPlayers.isEmpty()) { 43 | builder.setTitle(Text.translate("command.hiddenplayers.title").toString()); 44 | builder.setColor(Color.RED); 45 | builder.setDescription(Text.translate("command.hiddenplayers.no_players")); 46 | event.getHook().sendMessageEmbeds(builder.build()).setEphemeral(true).queue(); 47 | return; 48 | } 49 | 50 | ButtonEmbedPaginator.Builder paginator = MessageUtil.defaultPaginator(); 51 | 52 | /** 53 | * Use Pagination to avoid message limits 54 | */ 55 | MessageUtil.listBatches(hiddenPlayers.values().stream().toList(), 10).forEach(p -> { 56 | StringBuilder sb = new StringBuilder(); 57 | count.getAndIncrement(); 58 | builder.clear(); 59 | builder.setTitle(Text.translate("command.hiddenplayers.title_page", count.get(),(int) Math.ceil(((float) hiddenPlayers.size() / 10))).toString()); 60 | builder.setColor(Color.GREEN); 61 | 62 | p.forEach(account -> { 63 | sb.append("`").append(account.getDisplayName()).append("`"); 64 | sb.append(" - ").append(account.getType()); 65 | sb.append("\r\n"); 66 | }); 67 | 68 | builder.setDescription(sb.toString()); 69 | pages.add(builder.build()); 70 | }); 71 | 72 | paginator.setItems(pages); 73 | ButtonEmbedPaginator embedPaginator = paginator.build(); 74 | 75 | event.getHook().sendMessageEmbeds(pages.get(0)).setEphemeral(false).queue(success -> embedPaginator.paginate(success, 1)); 76 | } catch (Exception e) { 77 | event.getHook().sendMessage(Text.translate("error.command_failed").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 78 | BotController.INSTANCE.getLogger().error("Failed to run hidden player list command", e); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/hide/HidePlayerCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.discord.commands.slash.hide; 2 | 3 | import com.hypherionmc.sdlink.api.messaging.Result; 4 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 5 | import com.hypherionmc.sdlink.core.database.SDLinkAccount; 6 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 7 | import com.hypherionmc.sdlink.core.managers.DatabaseManager; 8 | import com.hypherionmc.sdlink.core.managers.HiddenPlayersManager; 9 | import com.hypherionmc.sdlink.util.translations.Text; 10 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 11 | import net.dv8tion.jda.api.entities.User; 12 | import net.dv8tion.jda.api.interactions.commands.OptionType; 13 | import net.dv8tion.jda.api.interactions.commands.build.OptionData; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | public final class HidePlayerCommand extends SDLinkSlashCommand { 19 | 20 | public HidePlayerCommand() { 21 | super(true); 22 | this.name = "hideplayer"; 23 | this.help = Text.translate("command.hideplayer.help").toString(); 24 | 25 | this.options = new ArrayList<>() {{ 26 | add(new OptionData(OptionType.USER, "user", "The user to make invisible").setRequired(true)); 27 | add(new OptionData(OptionType.BOOLEAN, "minecraft", "Hide the user in minecraft if they have a linked account").setRequired(false)); 28 | }}; 29 | } 30 | 31 | @Override 32 | protected void execute(SlashCommandEvent event) { 33 | event.deferReply(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 34 | User user = event.getOption("user").getAsUser(); 35 | boolean mc = event.hasOption("minecraft") && event.getOption("minecraft").getAsBoolean(); 36 | 37 | if (mc) { 38 | if (!SDLinkConfig.INSTANCE.accessControl.enabled && !SDLinkConfig.INSTANCE.accessControl.optionalVerification) { 39 | event.getHook().editOriginal(Text.translate("command.hideplayer.access_control").toString()).queue(); 40 | return; 41 | } 42 | 43 | List accounts = DatabaseManager.INSTANCE.getCollection(SDLinkAccount.class).stream().filter(a -> a.getDiscordID() != null && a.getDiscordID().equalsIgnoreCase(user.getId())).toList(); 44 | if (accounts.isEmpty()) { 45 | event.getHook().editOriginal(Text.translate("command.hideplayer.account_not_found", user.getAsMention()).toString()).queue(); 46 | } else { 47 | for (SDLinkAccount account : accounts) { 48 | HiddenPlayersManager.INSTANCE.hidePlayer(account.getUuid(), account.getDiscordID(), "minecraft"); 49 | } 50 | } 51 | } 52 | 53 | Result res = HiddenPlayersManager.INSTANCE.hidePlayer(user.getId(), user.getEffectiveName(), "discord"); 54 | event.getHook().editOriginal(res.getMessage()).queue(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/hide/UnhidePlayerCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.discord.commands.slash.hide; 2 | 3 | import com.hypherionmc.sdlink.api.messaging.Result; 4 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 5 | import com.hypherionmc.sdlink.core.database.SDLinkAccount; 6 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 7 | import com.hypherionmc.sdlink.core.managers.DatabaseManager; 8 | import com.hypherionmc.sdlink.core.managers.HiddenPlayersManager; 9 | import com.hypherionmc.sdlink.util.translations.Text; 10 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 11 | import net.dv8tion.jda.api.entities.User; 12 | import net.dv8tion.jda.api.interactions.commands.OptionType; 13 | import net.dv8tion.jda.api.interactions.commands.build.OptionData; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | public final class UnhidePlayerCommand extends SDLinkSlashCommand { 19 | 20 | public UnhidePlayerCommand() { 21 | super(true); 22 | this.name = "unhideplayer"; 23 | this.help = Text.translate("command.unhideplayer.help").toString(); 24 | 25 | this.options = new ArrayList<>() {{ 26 | add(new OptionData(OptionType.USER, "user", "The user to make visible").setRequired(true)); 27 | add(new OptionData(OptionType.BOOLEAN, "minecraft", "Unhide the user in minecraft if they have a linked account").setRequired(false)); 28 | }}; 29 | } 30 | 31 | @Override 32 | protected void execute(SlashCommandEvent event) { 33 | event.deferReply(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 34 | User user = event.getOption("user").getAsUser(); 35 | boolean mc = event.hasOption("minecraft") && event.getOption("minecraft").getAsBoolean(); 36 | 37 | if (mc) { 38 | if (!SDLinkConfig.INSTANCE.accessControl.enabled && !SDLinkConfig.INSTANCE.accessControl.optionalVerification) { 39 | event.getHook().editOriginal(Text.translate("command.hideplayer.access_control").toString()).queue(); 40 | return; 41 | } 42 | 43 | List accounts = DatabaseManager.INSTANCE.getCollection(SDLinkAccount.class).stream().filter(a -> a.getDiscordID() != null && a.getDiscordID().equalsIgnoreCase(user.getId())).toList(); 44 | if (accounts.isEmpty()) { 45 | event.getHook().editOriginal(Text.translate("command.hideplayer.account_not_found", user.getAsMention()).toString()).queue(); 46 | } else { 47 | for (SDLinkAccount account : accounts) { 48 | HiddenPlayersManager.INSTANCE.unhidePlayer(account.getUuid()); 49 | } 50 | } 51 | } 52 | 53 | Result res = HiddenPlayersManager.INSTANCE.unhidePlayer(user.getId()); 54 | event.getHook().editOriginal(res.getMessage()).queue(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/setup/ReloadCacheCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.discord.commands.slash.setup; 2 | 3 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 4 | import com.hypherionmc.sdlink.core.discord.BotController; 5 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 6 | import com.hypherionmc.sdlink.core.managers.CacheManager; 7 | import com.hypherionmc.sdlink.core.managers.ChannelManager; 8 | import com.hypherionmc.sdlink.util.translations.Text; 9 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 10 | 11 | public final class ReloadCacheCommand extends SDLinkSlashCommand { 12 | 13 | public ReloadCacheCommand() { 14 | super(true); 15 | this.name = "reloadcache"; 16 | this.help = Text.translate("command.reloadcache.help").toString(); 17 | } 18 | 19 | @Override 20 | protected void execute(SlashCommandEvent slashCommandEvent) { 21 | try { 22 | CacheManager.loadCache(); 23 | ChannelManager.loadChannels(); 24 | slashCommandEvent.reply(Text.translate("command.reloadcache.reloaded").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 25 | } catch (Exception e) { 26 | BotController.INSTANCE.getLogger().error("Failed to reload cache", e); 27 | slashCommandEvent.reply(Text.translate("command.reloadcache.not_reloaded").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/verification/StaffUnverifyCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.discord.commands.slash.verification; 6 | 7 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 8 | import com.hypherionmc.sdlink.api.messaging.Result; 9 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 10 | import com.hypherionmc.sdlink.core.database.SDLinkAccount; 11 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 12 | import com.hypherionmc.sdlink.core.managers.DatabaseManager; 13 | import com.hypherionmc.sdlink.util.translations.Text; 14 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 15 | import net.dv8tion.jda.api.entities.Member; 16 | import net.dv8tion.jda.api.entities.User; 17 | import net.dv8tion.jda.api.interactions.commands.OptionType; 18 | import net.dv8tion.jda.api.interactions.commands.build.OptionData; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | public final class StaffUnverifyCommand extends SDLinkSlashCommand { 24 | 25 | public StaffUnverifyCommand() { 26 | super(true); 27 | this.name = "staffunverify"; 28 | this.help = Text.translate("command.staffunverify.help").toString(); 29 | 30 | this.options = new ArrayList<>() {{ 31 | add(new OptionData(OptionType.USER, "discorduser", "The discord user the minecraft account belongs to").setRequired(true)); 32 | add(new OptionData(OptionType.STRING, "mcname", "The minecraft account of the linked user").setRequired(true)); 33 | }}; 34 | } 35 | 36 | @Override 37 | protected void execute(SlashCommandEvent event) { 38 | event.deferReply(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 39 | 40 | List accounts = DatabaseManager.INSTANCE.findAll(SDLinkAccount.class); 41 | 42 | if (accounts.isEmpty()) { 43 | event.getHook().sendMessage(Text.translate("error.no_db_accounts").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 44 | return; 45 | } 46 | 47 | String mcname = event.getOption("mcname").getAsString(); 48 | User user = event.getOption("discorduser").getAsUser(); 49 | 50 | Member member = event.getGuild().getMemberById(user.getId()); 51 | 52 | if (member == null) { 53 | event.getHook().sendMessage(Text.translate("error.not_a_member", user.getEffectiveName()).toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 54 | return; 55 | } 56 | 57 | SDLinkAccount account = accounts.stream().filter(a -> a.getUsername().equalsIgnoreCase(mcname) || a.getInGameName().equalsIgnoreCase(mcname)).findFirst().orElse(null); 58 | 59 | if (account == null) { 60 | event.getHook().editOriginal(Text.translate("error.not_match_found", mcname).toString()).queue(); 61 | return; 62 | } 63 | 64 | MinecraftAccount minecraftAccount = MinecraftAccount.of(account); 65 | Result result = minecraftAccount.unverifyAccount(member, event.getGuild()); 66 | event.getHook().editOriginal(result.getMessage()).queue(); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/verification/StaffVerifyAccountCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.discord.commands.slash.verification; 6 | 7 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 8 | import com.hypherionmc.sdlink.api.messaging.Result; 9 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 10 | import com.hypherionmc.sdlink.core.database.SDLinkAccount; 11 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 12 | import com.hypherionmc.sdlink.core.managers.DatabaseManager; 13 | import com.hypherionmc.sdlink.util.translations.Text; 14 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 15 | import net.dv8tion.jda.api.entities.Member; 16 | import net.dv8tion.jda.api.entities.User; 17 | import net.dv8tion.jda.api.interactions.commands.OptionType; 18 | import net.dv8tion.jda.api.interactions.commands.build.OptionData; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | public final class StaffVerifyAccountCommand extends SDLinkSlashCommand { 24 | 25 | public StaffVerifyAccountCommand() { 26 | super(true); 27 | this.name = "staffverify"; 28 | this.help = Text.translate("command.staffverify.help").toString(); 29 | 30 | this.options = new ArrayList<>() {{ 31 | add(new OptionData(OptionType.USER, "discorduser", "The discord user the minecraft account belongs to").setRequired(true)); 32 | add(new OptionData(OptionType.STRING, "mcname", "The minecraft account to link to the user").setRequired(true)); 33 | }}; 34 | } 35 | 36 | @Override 37 | protected void execute(SlashCommandEvent event) { 38 | event.deferReply(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 39 | 40 | List accounts = DatabaseManager.INSTANCE.findAll(SDLinkAccount.class); 41 | 42 | if (accounts.isEmpty()) { 43 | event.getHook().sendMessage(Text.translate("error.no_db_accounts").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 44 | return; 45 | } 46 | 47 | String mcname = event.getOption("mcname").getAsString(); 48 | User user = event.getOption("discorduser").getAsUser(); 49 | 50 | Member member = event.getGuild().getMemberById(user.getId()); 51 | 52 | if (member == null) { 53 | event.getHook().sendMessage(Text.translate("error.not_a_member", user.getEffectiveName()).toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 54 | return; 55 | } 56 | 57 | SDLinkAccount account = accounts.stream().filter(a -> a.getUsername().equalsIgnoreCase(mcname) || a.getInGameName().equalsIgnoreCase(mcname)).findFirst().orElse(null); 58 | 59 | if (account == null) { 60 | event.getHook().editOriginal(Text.translate("error.not_match_found", mcname).toString()).queue(); 61 | return; 62 | } 63 | 64 | MinecraftAccount minecraftAccount = MinecraftAccount.of(account); 65 | Result result = minecraftAccount.verifyAccount(member, event.getGuild()); 66 | event.getHook().sendMessage(result.getMessage()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/verification/UnverifyAccountSlashCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.discord.commands.slash.verification; 6 | 7 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 8 | import com.hypherionmc.sdlink.api.messaging.Result; 9 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 10 | import com.hypherionmc.sdlink.core.database.SDLinkAccount; 11 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 12 | import com.hypherionmc.sdlink.core.managers.DatabaseManager; 13 | import com.hypherionmc.sdlink.util.translations.Text; 14 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 15 | import net.dv8tion.jda.api.entities.Guild; 16 | import net.dv8tion.jda.api.entities.Member; 17 | 18 | import java.util.List; 19 | 20 | public final class UnverifyAccountSlashCommand extends SDLinkSlashCommand { 21 | 22 | public UnverifyAccountSlashCommand() { 23 | super(false); 24 | this.name = "unverify"; 25 | this.help = Text.translate("command.unverify.help").toString(); 26 | this.guildOnly = false; 27 | } 28 | 29 | @Override 30 | protected void execute(SlashCommandEvent event) { 31 | event.deferReply(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 32 | 33 | List accounts = DatabaseManager.INSTANCE.findAll(SDLinkAccount.class); 34 | 35 | if (accounts.isEmpty()) { 36 | event.getHook().sendMessage(Text.translate("error.no_db_accounts").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 37 | return; 38 | } 39 | 40 | Guild guild = event.isFromGuild() ? event.getGuild() : (event.getJDA().getGuilds().isEmpty() ? null : event.getJDA().getGuilds().get(0)); 41 | if (guild == null) { 42 | event.getHook().sendMessage(Text.translate("error.no_discord_server").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 43 | return; 44 | } 45 | 46 | Member m = event.isFromGuild() ? event.getMember() : guild.getMemberById(event.getUser().getId()); 47 | if (m == null) { 48 | event.getHook().sendMessage(Text.translate("error.not_a_member_of", guild.getName()).toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 49 | return; 50 | } 51 | 52 | boolean didUnverify = false; 53 | 54 | for (SDLinkAccount account : accounts) { 55 | if (account.getDiscordID() != null && account.getDiscordID().equalsIgnoreCase(m.getId())) { 56 | MinecraftAccount minecraftAccount = MinecraftAccount.of(account); 57 | Result result = minecraftAccount.unverifyAccount(m, guild); 58 | event.getHook().sendMessage(result.getMessage()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 59 | didUnverify = true; 60 | break; 61 | } 62 | } 63 | 64 | if (!didUnverify) 65 | event.getHook().sendMessage(Text.translate("command.unverify.failed").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/verification/VerifyAccountCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.discord.commands.slash.verification; 6 | 7 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 8 | import com.hypherionmc.sdlink.api.messaging.Result; 9 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 10 | import com.hypherionmc.sdlink.core.database.SDLinkAccount; 11 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 12 | import com.hypherionmc.sdlink.core.managers.DatabaseManager; 13 | import com.hypherionmc.sdlink.util.translations.Text; 14 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 15 | import net.dv8tion.jda.api.entities.Guild; 16 | import net.dv8tion.jda.api.entities.Member; 17 | import net.dv8tion.jda.api.interactions.commands.OptionType; 18 | import net.dv8tion.jda.api.interactions.commands.build.OptionData; 19 | 20 | import java.util.Collections; 21 | import java.util.List; 22 | 23 | public final class VerifyAccountCommand extends SDLinkSlashCommand { 24 | 25 | public VerifyAccountCommand() { 26 | super(false); 27 | this.name = "verify"; 28 | this.help = Text.translate("command.verify.help").toString(); 29 | this.guildOnly = false; 30 | 31 | this.options = Collections.singletonList(new OptionData(OptionType.INTEGER, "code", "The verification code from the Minecraft Kick Message").setRequired(true)); 32 | } 33 | 34 | @Override 35 | protected void execute(SlashCommandEvent event) { 36 | event.deferReply(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 37 | 38 | int mcCode = event.getOption("code") != null ? event.getOption("code").getAsInt() : 0; 39 | 40 | if (mcCode == 0) { 41 | event.getHook().sendMessage(Text.translate("command.verify.missing_code").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 42 | return; 43 | } 44 | 45 | List accounts = DatabaseManager.INSTANCE.findAll(SDLinkAccount.class); 46 | 47 | if (accounts.isEmpty()) { 48 | event.getHook().sendMessage(Text.translate("error.no_db_accounts").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 49 | return; 50 | } 51 | 52 | Guild guild = event.isFromGuild() ? event.getGuild() : (event.getJDA().getGuilds().isEmpty() ? null : event.getJDA().getGuilds().get(0)); 53 | if (guild == null) { 54 | event.getHook().sendMessage(Text.translate("error.no_discord_server").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 55 | return; 56 | } 57 | 58 | Member m = event.isFromGuild() ? event.getMember() : guild.getMemberById(event.getUser().getId()); 59 | if (m == null) { 60 | event.getHook().sendMessage(Text.translate("error.not_a_member_of", guild.getName()).toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 61 | return; 62 | } 63 | 64 | boolean didVerify = false; 65 | 66 | for (SDLinkAccount account : accounts) { 67 | if (account.getVerifyCode() == null) 68 | continue; 69 | 70 | if (accounts.stream().anyMatch(a -> a.getDiscordID() != null && a.getDiscordID().equals(m.getId())) && !SDLinkConfig.INSTANCE.accessControl.allowMultipleAccounts) { 71 | event.getHook().sendMessage(Text.translate("command.verify.already_verified").toString()).queue(); 72 | return; 73 | } 74 | 75 | if (account.getVerifyCode().equalsIgnoreCase(String.valueOf(mcCode))) { 76 | MinecraftAccount minecraftAccount = MinecraftAccount.of(account); 77 | Result result = minecraftAccount.verifyAccount(m, guild); 78 | event.getHook().sendMessage(result.getMessage()).setEphemeral(true).queue(); 79 | didVerify = true; 80 | break; 81 | } 82 | } 83 | 84 | if (!didVerify) 85 | event.getHook().sendMessage(Text.translate("command.verify.failed").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/commands/slash/verification/ViewVerifiedAccounts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.discord.commands.slash.verification; 6 | 7 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 8 | import com.hypherionmc.sdlink.core.database.SDLinkAccount; 9 | import com.hypherionmc.sdlink.core.discord.BotController; 10 | import com.hypherionmc.sdlink.core.discord.commands.slash.SDLinkSlashCommand; 11 | import com.hypherionmc.sdlink.core.managers.DatabaseManager; 12 | import com.hypherionmc.sdlink.util.MessageUtil; 13 | import com.hypherionmc.sdlink.util.translations.Text; 14 | import com.jagrosh.jdautilities.command.SlashCommandEvent; 15 | import com.jagrosh.jdautilities.menu.ButtonEmbedPaginator; 16 | import net.dv8tion.jda.api.EmbedBuilder; 17 | import net.dv8tion.jda.api.entities.Member; 18 | import net.dv8tion.jda.api.entities.MessageEmbed; 19 | 20 | import java.awt.*; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.concurrent.atomic.AtomicInteger; 24 | 25 | /** 26 | * @author HypherionSA 27 | * Staff Command to view a list of Linked Minecraft and Discord accounts 28 | */ 29 | public final class ViewVerifiedAccounts extends SDLinkSlashCommand { 30 | 31 | public ViewVerifiedAccounts() { 32 | super(true); 33 | 34 | this.name = "verifiedaccounts"; 35 | this.help = Text.translate("command.verifiedaccounts.help").toString(); 36 | } 37 | 38 | @Override 39 | protected void execute(SlashCommandEvent event) { 40 | event.deferReply(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 41 | try { 42 | ButtonEmbedPaginator.Builder paginator = MessageUtil.defaultPaginator(); 43 | 44 | List accounts = DatabaseManager.INSTANCE.findAll(SDLinkAccount.class); 45 | 46 | EmbedBuilder builder = new EmbedBuilder(); 47 | ArrayList pages = new ArrayList<>(); 48 | AtomicInteger count = new AtomicInteger(); 49 | 50 | if (accounts.isEmpty()) { 51 | event.getHook().sendMessage(Text.translate("command.verifiedaccounts.no_accounts").toString()).setEphemeral(true).queue(); 52 | return; 53 | } 54 | 55 | MessageUtil.listBatches(accounts, 10).forEach(itm -> { 56 | count.getAndIncrement(); 57 | builder.clear(); 58 | builder.setTitle(Text.translate("command.verifiedaccounts.title_page", count.get(),(int) Math.ceil(((float) accounts.size() / 10))).toString()); 59 | builder.setColor(Color.GREEN); 60 | StringBuilder sBuilder = new StringBuilder(); 61 | 62 | itm.forEach(v -> { 63 | Member member = null; 64 | 65 | if (v.getDiscordID() != null && !v.getDiscordID().isEmpty()) { 66 | member = event.getGuild().getMemberById(v.getDiscordID()); 67 | } 68 | 69 | sBuilder.append(v.getUsername()).append(!v.getInGameName().equalsIgnoreCase(v.getUsername()) ? " (" + v.getInGameName() + " )" : "").append(" -> ").append(member == null ? "Unlinked" : member.getAsMention()).append("\r\n"); 70 | }); 71 | builder.setDescription(sBuilder); 72 | pages.add(builder.build()); 73 | }); 74 | 75 | paginator.setItems(pages); 76 | ButtonEmbedPaginator embedPaginator = paginator.build(); 77 | 78 | event.getHook().sendMessageEmbeds(pages.get(0)).setEphemeral(false).queue(success -> embedPaginator.paginate(success, 1)); 79 | } catch (Exception e) { 80 | event.getHook().sendMessage(Text.translate("error.command_failed").toString()).setEphemeral(SDLinkConfig.INSTANCE.botConfig.silentReplies).queue(); 81 | BotController.INSTANCE.getLogger().error("Failed to run verifiedaccounts command", e); 82 | } 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/discord/hooks/DiscordRoleHooks.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.discord.hooks; 2 | 3 | import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; 4 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 5 | import com.hypherionmc.sdlink.api.messaging.Result; 6 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 7 | import com.hypherionmc.sdlink.core.config.impl.TriggerCommandsConfig; 8 | import com.hypherionmc.sdlink.core.database.SDLinkAccount; 9 | import com.hypherionmc.sdlink.core.discord.BotController; 10 | import com.hypherionmc.sdlink.core.managers.DatabaseManager; 11 | import com.hypherionmc.sdlink.platform.SDLinkMCPlatform; 12 | import com.hypherionmc.sdlink.server.ServerEvents; 13 | import lombok.AccessLevel; 14 | import lombok.NoArgsConstructor; 15 | import net.dv8tion.jda.api.entities.Role; 16 | import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleAddEvent; 17 | import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleRemoveEvent; 18 | import org.jetbrains.annotations.NotNull; 19 | 20 | import java.util.List; 21 | import java.util.Optional; 22 | import java.util.concurrent.CompletableFuture; 23 | 24 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 25 | public final class DiscordRoleHooks { 26 | 27 | public static final DiscordRoleHooks INSTANCE = new DiscordRoleHooks(); 28 | 29 | public void onRoleAdded(@NotNull GuildMemberRoleAddEvent event) { 30 | runCommandChecks(event.getRoles(), SDLinkConfig.INSTANCE.triggerCommands.roleAdded, "roleAdded", event.getMember().getId()); 31 | } 32 | 33 | public void onRoleRemoved(@NotNull GuildMemberRoleRemoveEvent event) { 34 | runCommandChecks(event.getRoles(), SDLinkConfig.INSTANCE.triggerCommands.roleRemoved, "roleRemoved", event.getMember().getId()); 35 | } 36 | 37 | private void runCommandChecks(List roles, List triggers, String section, String memberId) { 38 | if (!(SDLinkConfig.INSTANCE.accessControl.enabled || SDLinkConfig.INSTANCE.accessControl.optionalVerification) || !SDLinkConfig.INSTANCE.triggerCommands.enabled) 39 | return; 40 | 41 | try { 42 | List accounts = DatabaseManager.INSTANCE.getCollection(SDLinkAccount.class); 43 | 44 | if (accounts.isEmpty()) 45 | return; 46 | 47 | Optional account = accounts.stream().filter(d -> d.getDiscordID() != null && d.getDiscordID().equalsIgnoreCase(memberId)).findFirst(); 48 | 49 | account.ifPresent(acc -> { 50 | MinecraftAccount mcAccount = MinecraftAccount.of(acc); 51 | 52 | for (Role role : roles) { 53 | Optional triggerHolder = triggers.stream().filter(r -> r.discordRole.equalsIgnoreCase(role.getName()) || r.discordRole.equalsIgnoreCase(role.getId())).findFirst(); 54 | if (triggerHolder.isEmpty()) 55 | continue; 56 | 57 | triggerHolder.get().minecraftCommand.forEach(cmd -> executeCommand( 58 | cmd.replace("%player%", mcAccount.getUsername()) 59 | .replace("%role%", role.getName()) 60 | .replace("%ingamename%", acc.getInGameName()) 61 | .replace("%rolenospaces%", role.getName().replace(" ", ""))) 62 | ); 63 | } 64 | }); 65 | } catch (Exception e) { 66 | BotController.INSTANCE.getLogger().error("Failed to run {} trigger", section, e); 67 | } 68 | } 69 | 70 | private static void executeCommand(String command) { 71 | CompletableFuture result = new CompletableFuture<>(); 72 | SDLinkMCPlatform.INSTANCE.executeCommand(command, 4, "", result); 73 | 74 | result.thenAccept(res -> { 75 | if (res.isError()) { 76 | BotController.INSTANCE.getLogger().error("Failed to trigger command {}: {}", command, res.getMessage()); 77 | } 78 | }); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/editor/ConfigEditorClient.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.editor; 2 | 3 | import com.hypherionmc.craterlib.nojang.commands.BridgedCommandSourceStack; 4 | import com.hypherionmc.sdlink.core.discord.BotController; 5 | import com.hypherionmc.sdlink.util.EncryptionUtil; 6 | import com.neovisionaries.ws.client.WebSocket; 7 | import com.neovisionaries.ws.client.WebSocketFactory; 8 | import lombok.AccessLevel; 9 | import lombok.NoArgsConstructor; 10 | 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public final class ConfigEditorClient { 13 | 14 | public static final ConfigEditorClient INSTANCE = new ConfigEditorClient(); 15 | 16 | private WebSocket webSocket; 17 | 18 | public void openConnection(BridgedCommandSourceStack sourceStack) { 19 | String identifier = EncryptionUtil.getSaltString(); 20 | 21 | try { 22 | closeServer(); 23 | webSocket = new WebSocketFactory().createSocket("wss://editor.firstdark.dev/ws/config?identifier=" + identifier); 24 | webSocket.setPingInterval(10000); 25 | webSocket.addListener(new ConfigEditorWSEvents(identifier, sourceStack)); 26 | webSocket.connect(); 27 | } catch (Exception e) { 28 | BotController.INSTANCE.getLogger().error("Failed to open connection to Config Editor", e); 29 | } 30 | } 31 | 32 | public void closeServer() { 33 | try { 34 | if (webSocket != null && webSocket.isOpen()) 35 | webSocket.disconnect(); 36 | } catch (Exception ignored) {} 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/experimental/ExperimentalFeatures.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.experimental; 2 | 3 | import com.hypherionmc.sdlink.core.discord.BotController; 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.File; 8 | import java.io.FileReader; 9 | import java.util.Properties; 10 | 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public final class ExperimentalFeatures { 13 | 14 | public static final ExperimentalFeatures INSTANCE = new ExperimentalFeatures(); 15 | 16 | public boolean RELAY_SERVER = false; 17 | 18 | public void loadFeatures() { 19 | File f = new File("./sdlinkstorage/IKNOWWHATIMDOING"); 20 | 21 | if (!f.exists()) 22 | return; 23 | 24 | try { 25 | Properties props = new Properties(); 26 | props.load(new FileReader(f)); 27 | 28 | RELAY_SERVER = Boolean.parseBoolean(props.getProperty("RELAY_SERVER", "false")); 29 | } catch (Exception e) { 30 | BotController.INSTANCE.getLogger().error("Failed to load Experimental Features", e); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/jsondb/annotations/Document.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.jsondb.annotations; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * @author HypherionSA 7 | * Marker annotation to mark database tables 8 | */ 9 | @Inherited 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.TYPE}) 12 | public @interface Document { 13 | String collection(); 14 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/jsondb/annotations/Id.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.jsondb.annotations; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | 7 | import static java.lang.annotation.ElementType.*; 8 | 9 | /** 10 | * @author HypherionSA 11 | * Marker annotation to indicate which field is the ID field 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(value = {FIELD, METHOD, ANNOTATION_TYPE}) 15 | public @interface Id {} -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/managers/CacheManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.managers; 6 | 7 | import com.hypherionmc.sdlink.api.messaging.MessageType; 8 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 9 | import com.hypherionmc.sdlink.core.config.impl.MessageChannelConfig; 10 | import com.hypherionmc.sdlink.core.discord.BotController; 11 | import lombok.Getter; 12 | import net.dv8tion.jda.api.JDA; 13 | import net.dv8tion.jda.api.entities.Member; 14 | import net.dv8tion.jda.api.entities.channel.ChannelType; 15 | import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; 16 | 17 | import java.util.HashMap; 18 | import java.util.HashSet; 19 | import java.util.Set; 20 | 21 | public final class CacheManager { 22 | 23 | @Getter 24 | private static final HashMap serverChannels = new HashMap<>(); 25 | @Getter 26 | private static final HashMap serverRoles = new HashMap<>(); 27 | @Getter 28 | private static final HashMap userCache = new HashMap<>(); 29 | @Getter 30 | private static final Set discordMembers = new HashSet<>(); 31 | @Getter 32 | private static final HashMap customEmotes = new HashMap<>(); 33 | 34 | @Getter 35 | public static final HashMap messageDestinations = new HashMap<>(); 36 | 37 | public static void loadCache() { 38 | loadChannelCache(); 39 | loadRoleCache(); 40 | loadUserCache(); 41 | loadEmoteCache(); 42 | } 43 | 44 | public static void loadChannelCache() { 45 | reloadChannelConfigCache(); 46 | serverChannels.clear(); 47 | 48 | JDA jda = BotController.INSTANCE.getJDA(); 49 | 50 | if (jda.getGuilds().isEmpty()) 51 | return; 52 | 53 | jda.getGuilds().get(0).getChannels(false).forEach(c -> { 54 | if (c.getType() != ChannelType.CATEGORY) { 55 | serverChannels.put("#" + c.getName(), c.getAsMention()); 56 | } 57 | }); 58 | } 59 | 60 | public static void loadRoleCache() { 61 | serverRoles.clear(); 62 | 63 | JDA jda = BotController.INSTANCE.getJDA(); 64 | 65 | if (jda.getGuilds().isEmpty()) 66 | return; 67 | 68 | jda.getGuilds().get(0).getRoles().forEach(r -> { 69 | if (r.isMentionable() && !r.isManaged()) { 70 | serverRoles.put("@" + r.getName(), r.getAsMention()); 71 | } 72 | }); 73 | } 74 | 75 | public static void loadUserCache() { 76 | userCache.clear(); 77 | discordMembers.clear(); 78 | 79 | JDA jda = BotController.INSTANCE.getJDA(); 80 | 81 | if (jda.getGuilds().isEmpty()) 82 | return; 83 | 84 | jda.getGuilds().get(0).getMembers().forEach(r -> { 85 | userCache.put("@" + r.getEffectiveName(), r.getAsMention()); 86 | discordMembers.add(r); 87 | }); 88 | } 89 | 90 | public static void loadEmoteCache() { 91 | customEmotes.clear(); 92 | 93 | JDA jda = BotController.INSTANCE.getJDA(); 94 | 95 | if (jda.getGuilds().isEmpty()) 96 | return; 97 | 98 | jda.getGuilds().get(0).getEmojis().forEach(e -> customEmotes.put(":" + e.getName() + ":", e)); 99 | } 100 | 101 | public static void reloadChannelConfigCache() { 102 | try { 103 | messageDestinations.clear(); 104 | messageDestinations.put(MessageType.CHAT, SDLinkConfig.INSTANCE.messageDestinations.chat); 105 | messageDestinations.put(MessageType.START, SDLinkConfig.INSTANCE.messageDestinations.start); 106 | messageDestinations.put(MessageType.STOP, SDLinkConfig.INSTANCE.messageDestinations.stop); 107 | messageDestinations.put(MessageType.JOIN, SDLinkConfig.INSTANCE.messageDestinations.join); 108 | messageDestinations.put(MessageType.LEAVE, SDLinkConfig.INSTANCE.messageDestinations.leave); 109 | messageDestinations.put(MessageType.ADVANCEMENTS, SDLinkConfig.INSTANCE.messageDestinations.advancements); 110 | messageDestinations.put(MessageType.DEATH, SDLinkConfig.INSTANCE.messageDestinations.death); 111 | messageDestinations.put(MessageType.COMMANDS, SDLinkConfig.INSTANCE.messageDestinations.commands); 112 | messageDestinations.put(MessageType.WHITELIST, SDLinkConfig.INSTANCE.messageDestinations.whitelist); 113 | messageDestinations.put(MessageType.CUSTOM, SDLinkConfig.INSTANCE.messageDestinations.custom); 114 | } catch (Exception ignored) {} 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/managers/ChannelManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.managers; 6 | 7 | import com.hypherionmc.sdlink.api.messaging.MessageDestination; 8 | import com.hypherionmc.sdlink.api.messaging.MessageType; 9 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 10 | import com.hypherionmc.sdlink.core.config.impl.MessageChannelConfig; 11 | import com.hypherionmc.sdlink.core.discord.BotController; 12 | import com.hypherionmc.sdlink.util.EncryptionUtil; 13 | import lombok.Getter; 14 | import net.dv8tion.jda.api.JDA; 15 | import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | /** 22 | * @author HypherionSA 23 | * Load and Cache configured channels for later use 24 | */ 25 | public final class ChannelManager { 26 | 27 | private static final HashMap channelMap = new HashMap<>(); 28 | private static final HashMap overrideChannels = new HashMap<>(); 29 | 30 | @Getter 31 | private static GuildMessageChannel consoleChannel; 32 | 33 | /** 34 | * Load configured channel, while always defaulting back to ChatChannel for channels that aren't configured 35 | */ 36 | public static void loadChannels() { 37 | channelMap.clear(); 38 | overrideChannels.clear(); 39 | 40 | JDA jda = BotController.INSTANCE.getJDA(); 41 | 42 | GuildMessageChannel chatChannel = jda.getChannelById(GuildMessageChannel.class, SDLinkConfig.INSTANCE.channelsAndWebhooks.channels.chatChannelID.isBlank() ? "0" : SDLinkConfig.INSTANCE.channelsAndWebhooks.channels.chatChannelID); 43 | GuildMessageChannel eventChannel = jda.getChannelById(GuildMessageChannel.class, SDLinkConfig.INSTANCE.channelsAndWebhooks.channels.eventsChannelID.isBlank() ? "0" : SDLinkConfig.INSTANCE.channelsAndWebhooks.channels.eventsChannelID); 44 | consoleChannel = jda.getChannelById(GuildMessageChannel.class, SDLinkConfig.INSTANCE.channelsAndWebhooks.channels.consoleChannelID.isBlank() ? "0" : SDLinkConfig.INSTANCE.channelsAndWebhooks.channels.consoleChannelID); 45 | 46 | if (chatChannel != null) { 47 | channelMap.put(MessageDestination.CHAT, chatChannel); 48 | } 49 | 50 | channelMap.put(MessageDestination.EVENT, eventChannel != null ? eventChannel : chatChannel); 51 | channelMap.put(MessageDestination.CONSOLE, consoleChannel != null ? consoleChannel : chatChannel); 52 | 53 | for (Map.Entry d : CacheManager.messageDestinations.entrySet()) { 54 | String override = EncryptionUtil.INSTANCE.decrypt(d.getValue().override); 55 | if (!d.getValue().channel.isOverride() || d.getValue().override == null || override.startsWith("http")) 56 | continue; 57 | 58 | String id = d.getValue().override; 59 | if (overrideChannels.containsKey(d.getKey())) 60 | continue; 61 | 62 | GuildMessageChannel channel = jda.getChannelById(GuildMessageChannel.class, id); 63 | if (channel == null) { 64 | BotController.INSTANCE.getLogger().error("Failed to load override channel {} for {}", id, d.getKey().name()); 65 | continue; 66 | } 67 | 68 | BotController.INSTANCE.getLogger().info("Using channel override {} for {}", channel.getName(), d.getKey().name()); 69 | overrideChannels.put(d.getKey(), channel); 70 | } 71 | } 72 | 73 | @Nullable 74 | public static GuildMessageChannel getOverride(MessageType type) { 75 | if (overrideChannels.get(type) == null) 76 | return null; 77 | 78 | return overrideChannels.get(type); 79 | } 80 | 81 | @Nullable 82 | public static GuildMessageChannel getDestinationChannel(MessageDestination destination) { 83 | if (channelMap.get(destination) == null) 84 | return null; 85 | 86 | return channelMap.get(destination); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/managers/DatabaseManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.managers; 6 | 7 | import com.hypherionmc.sdlink.core.database.HiddenPlayers; 8 | import com.hypherionmc.sdlink.core.database.SDLinkAccount; 9 | import com.hypherionmc.sdlink.core.jsondb.JsonDatabase; 10 | import com.hypherionmc.sdlink.core.jsondb.annotations.Document; 11 | 12 | import java.util.LinkedHashSet; 13 | import java.util.List; 14 | import java.util.Set; 15 | 16 | /** 17 | * @author HypherionSA 18 | * Helper class to initialize the JSON database 19 | */ 20 | public final class DatabaseManager { 21 | 22 | public static final DatabaseManager INSTANCE = new DatabaseManager(); 23 | 24 | private final JsonDatabase sdlinkDatabase = new JsonDatabase("sdlinkstorage"); 25 | 26 | private final Set> tables = new LinkedHashSet<>() {{ 27 | add(SDLinkAccount.class); 28 | add(HiddenPlayers.class); 29 | }}; 30 | 31 | DatabaseManager() { 32 | sdlinkDatabase.setupDB(tables); 33 | } 34 | 35 | public void initialize() { 36 | tables.forEach(t -> sdlinkDatabase.reloadCollection(t.getAnnotation(Document.class).collection(), t)); 37 | } 38 | 39 | public void updateEntry(Object t) { 40 | sdlinkDatabase.upsert(t); 41 | reload(t.getClass()); 42 | } 43 | 44 | public void deleteEntry(Object t) { 45 | sdlinkDatabase.remove(t); 46 | reload(t.getClass()); 47 | } 48 | 49 | public void deleteEntry(Object t, Class clazz) { 50 | sdlinkDatabase.remove(t); 51 | reload(t.getClass()); 52 | } 53 | 54 | private void reload(Class clazz) { 55 | sdlinkDatabase.reloadCollection(clazz.getAnnotation(Document.class).collection(), clazz); 56 | } 57 | 58 | public T findById(Object id, Class entityClass) { 59 | reload(entityClass); 60 | return sdlinkDatabase.findById(id, entityClass); 61 | } 62 | 63 | public List getCollection(Class entityClass) { 64 | reload(entityClass); 65 | return sdlinkDatabase.getCollection(entityClass); 66 | } 67 | 68 | public List findAll(Class tClass) { 69 | reload(tClass); 70 | return sdlinkDatabase.getCollection(tClass); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/managers/EmbedManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.managers; 6 | 7 | import com.google.gson.Gson; 8 | import com.google.gson.GsonBuilder; 9 | import com.hypherionmc.sdlink.core.discord.BotController; 10 | import com.hypherionmc.sdlink.core.messaging.embeds.DiscordEmbed; 11 | import org.apache.commons.io.FileUtils; 12 | 13 | import java.io.File; 14 | import java.io.FileWriter; 15 | import java.io.IOException; 16 | import java.io.Writer; 17 | import java.nio.charset.StandardCharsets; 18 | import java.util.concurrent.ConcurrentHashMap; 19 | 20 | public final class EmbedManager { 21 | 22 | private static final File embedDir = new File("./config/simple-discord-link/embeds"); 23 | public static final Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create(); 24 | private static final ConcurrentHashMap embeds = new ConcurrentHashMap<>(); 25 | 26 | public static void init() { 27 | embeds.clear(); 28 | embedDir.mkdirs(); 29 | defaultEmbeds(); 30 | 31 | if (embedDir.listFiles() == null) 32 | return; 33 | 34 | for (File file : embedDir.listFiles()) { 35 | if (file.isFile() && file.getName().endsWith(".json")) { 36 | try { 37 | String json = FileUtils.readFileToString(file, StandardCharsets.UTF_8); 38 | embeds.put(file.getName().replace(".json", ""), json); 39 | } catch (Exception e) { 40 | BotController.INSTANCE.getLogger().error("Failed to load custom embed {}", file.getName(), e); 41 | } 42 | } 43 | } 44 | } 45 | 46 | public static String getEmbed(String key) { 47 | return embeds.get(key); 48 | } 49 | 50 | private static void defaultEmbeds() { 51 | File defaultEmbed = new File(embedDir.getAbsolutePath() + File.separator + "default.json"); 52 | if (!defaultEmbed.exists()) { 53 | DiscordEmbed embed = new DiscordEmbed(); 54 | 55 | DiscordEmbed.Author author = new DiscordEmbed.Author(); 56 | author.name = "%author%"; 57 | author.url = null; 58 | author.icon_url = "%avatar%"; 59 | embed.author = author; 60 | embed.description = "%message_contents%"; 61 | embed.color = "#000000"; 62 | writeToFile(defaultEmbed, embed); 63 | } 64 | } 65 | 66 | private static void writeToFile(File file, Object data) { 67 | try (Writer writer = new FileWriter(file)) { 68 | gson.toJson(data, writer); 69 | } catch (IOException e) { 70 | throw new RuntimeException(e); 71 | } 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/managers/HiddenPlayersManager.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.managers; 2 | 3 | import com.hypherionmc.sdlink.SDLinkConstants; 4 | import com.hypherionmc.sdlink.api.messaging.Result; 5 | import com.hypherionmc.sdlink.core.database.HiddenPlayers; 6 | import com.hypherionmc.sdlink.util.translations.Text; 7 | import lombok.Getter; 8 | 9 | import java.util.HashMap; 10 | import java.util.LinkedHashMap; 11 | 12 | @Getter 13 | public final class HiddenPlayersManager { 14 | 15 | public static final HiddenPlayersManager INSTANCE = new HiddenPlayersManager(); 16 | private final HashMap hiddenPlayers = new LinkedHashMap<>(); 17 | 18 | private HiddenPlayersManager() {} 19 | 20 | public void loadHiddenPlayers() { 21 | hiddenPlayers.clear(); 22 | DatabaseManager.INSTANCE.getCollection(HiddenPlayers.class).forEach(p -> hiddenPlayers.put(p.getIdentifier(), p)); 23 | } 24 | 25 | public Result hidePlayer(String identifier, String displayName, String type) { 26 | try { 27 | HiddenPlayers player = HiddenPlayers.of(identifier, displayName, type); 28 | DatabaseManager.INSTANCE.updateEntry(player); 29 | hiddenPlayers.put(identifier, player); 30 | return Result.success(Text.translate("hiding.now_hidden", displayName)); 31 | } catch (Exception e) { 32 | SDLinkConstants.LOGGER.error("Failed to hide player {}", displayName, e); 33 | return Result.error(Text.translate("hiding.failed", e.getMessage())); 34 | } 35 | } 36 | 37 | public Result unhidePlayer(String identifier) { 38 | try { 39 | HiddenPlayers player = DatabaseManager.INSTANCE.findById(identifier, HiddenPlayers.class); 40 | 41 | if (player == null) { 42 | return Result.error(Text.translate("hiding.not_hidden")); 43 | } 44 | 45 | hiddenPlayers.remove(identifier); 46 | DatabaseManager.INSTANCE.deleteEntry(player); 47 | return Result.success(Text.translate("hiding.unhidden", player.getDisplayName())); 48 | } catch (Exception e) { 49 | SDLinkConstants.LOGGER.error("Failed to unhide player {}", identifier, e); 50 | return Result.error(Text.translate("hiding.unhide_failed", e.getMessage())); 51 | } 52 | } 53 | 54 | public boolean isPlayerHidden(String identifier) { 55 | return hiddenPlayers.containsKey(identifier); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/managers/SpamManager.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.managers; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | import java.util.Set; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | import java.util.concurrent.ScheduledExecutorService; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * @author HypherionSA 13 | * Basic Message Spam Detector 14 | */ 15 | public final class SpamManager { 16 | 17 | private final ConcurrentHashMap> messageTimestamps = new ConcurrentHashMap<>(); 18 | private final Set blockedMessages = new HashSet<>(); 19 | 20 | private final int threshold; 21 | private final int timeWindowMillis; 22 | private final int blockMillis; 23 | private final ScheduledExecutorService executor; 24 | 25 | public SpamManager(int threshold, int timeWindowMillis, int blockMillis, ScheduledExecutorService executor) { 26 | this.threshold = threshold; 27 | this.timeWindowMillis = timeWindowMillis; 28 | this.blockMillis = blockMillis; 29 | this.executor = executor; 30 | startSpamChecker(); 31 | } 32 | 33 | public void receiveMessage(String message) { 34 | long currentTime = System.currentTimeMillis(); 35 | 36 | messageTimestamps.compute(message, (msg, timestamps) -> { 37 | if (timestamps == null) 38 | timestamps = new ArrayList<>(); 39 | 40 | timestamps.add(currentTime); 41 | return new ArrayList<>( 42 | timestamps.stream() 43 | .filter(timestamp -> currentTime - timestamp <= timeWindowMillis) 44 | .toList() 45 | ); 46 | }); 47 | 48 | if (messageTimestamps.get(message).size() >= threshold) { 49 | blockedMessages.add(message); 50 | } 51 | } 52 | 53 | public boolean isBlocked(String message) { 54 | return blockedMessages.contains(message); 55 | } 56 | 57 | private void startSpamChecker() { 58 | executor.scheduleAtFixedRate(() -> { 59 | long currentTime = System.currentTimeMillis(); 60 | blockedMessages.removeIf(message -> { 61 | List timestamps = messageTimestamps.getOrDefault(message, new ArrayList<>()); 62 | timestamps = new ArrayList<>( 63 | timestamps.stream() 64 | .filter(timestamp -> currentTime - timestamp <= timeWindowMillis) 65 | .toList() 66 | ); 67 | messageTimestamps.put(message, timestamps); 68 | return timestamps.size() < threshold; 69 | }); 70 | }, blockMillis, blockMillis, TimeUnit.MILLISECONDS); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/messaging/SDLinkWebhookClientBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.messaging; 6 | 7 | import club.minnced.discord.webhook.WebhookClientBuilder; 8 | 9 | /** 10 | * @author HypherionSA 11 | * Wrapped {@link WebhookClientBuilder} for our webhooks 12 | */ 13 | public final class SDLinkWebhookClientBuilder extends WebhookClientBuilder { 14 | 15 | public SDLinkWebhookClientBuilder(String name, String url) { 16 | super(url); 17 | 18 | this.setThreadFactory((job) -> { 19 | Thread thread = new Thread(job); 20 | thread.setName(name + " Webhook Thread"); 21 | thread.setDaemon(true); 22 | return thread; 23 | }); 24 | this.setWait(false); 25 | } 26 | 27 | public SDLinkWebhookClientBuilder setThreadChannelID(String id) { 28 | this.setThreadId(Long.parseLong(id)); 29 | return this; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/messaging/embeds/DiscordEmbed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.messaging.embeds; 6 | 7 | import java.util.ArrayList; 8 | 9 | public final class DiscordEmbed { 10 | 11 | public String color; 12 | public String title; 13 | public String url; 14 | public Author author; 15 | public String description; 16 | public Thumbnail thumbnail; 17 | public ArrayList fields; 18 | public Image image; 19 | public int timestamp; 20 | public Footer footer; 21 | 22 | public static class Author { 23 | public String name; 24 | public String icon_url; 25 | public String url; 26 | } 27 | 28 | public static class Field { 29 | public String name; 30 | public String value; 31 | public boolean inline; 32 | } 33 | 34 | public static class Footer { 35 | public String text; 36 | public String icon_url; 37 | } 38 | 39 | public static class Image { 40 | public String url; 41 | } 42 | 43 | public static class Thumbnail { 44 | public String url; 45 | } 46 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/relay/DataMessage.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.relay; 2 | 3 | import com.hypherionmc.craterlib.utils.ChatUtils; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import org.jetbrains.annotations.Nullable; 8 | import shadow.kyori.adventure.text.Component; 9 | 10 | import java.util.UUID; 11 | 12 | @NoArgsConstructor 13 | @AllArgsConstructor(staticName = "of") 14 | public class DataMessage { 15 | 16 | private String displayName; 17 | @Getter private String username; 18 | private String message; 19 | @Getter private UUID uuid; 20 | private String additional; 21 | @Getter private boolean isFromServer; 22 | 23 | public static DataMessage of(Component displayName, String username, @Nullable Component message, UUID uuid, boolean isFromServer) { 24 | return of (displayName, username, message, uuid, null, isFromServer); 25 | } 26 | 27 | public static DataMessage of(Component displayName, String username, @Nullable Component message, UUID uuid, @Nullable Component additional, boolean isFromServer) { 28 | return of(ChatUtils.getAdventureSerializer().serialize(displayName), username, message == null ? null : ChatUtils.getAdventureSerializer().serialize(message), uuid, additional == null ? null : ChatUtils.getAdventureSerializer().serialize(additional), isFromServer); 29 | } 30 | 31 | public Component displayName() { 32 | return ChatUtils.getAdventureSerializer().deserialize(displayName); 33 | } 34 | 35 | public Component message() { 36 | return ChatUtils.getAdventureSerializer().deserialize(message); 37 | } 38 | 39 | public Component additional() { 40 | return ChatUtils.getAdventureSerializer().deserialize(additional); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/relay/RelayMessage.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.core.relay; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | @AllArgsConstructor(staticName = "of") 8 | @NoArgsConstructor 9 | @Getter 10 | public final class RelayMessage { 11 | 12 | private MessageType type; 13 | private String serverName; 14 | private DataMessage data; 15 | private String message; 16 | 17 | public static RelayMessage of(MessageType type, String serverName, DataMessage data) { 18 | return new RelayMessage(type, serverName, data, null); 19 | } 20 | 21 | public enum MessageType { 22 | CHAT, 23 | JOIN, 24 | LEAVE, 25 | ADVANCEMENT, 26 | DEATH, 27 | DISCORD 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/core/services/SDLinkPlatform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.core.services; 6 | 7 | import com.hypherionmc.sdlink.server.SDLinkMinecraftBridge; 8 | 9 | /** 10 | * @author HypherionSA 11 | * Service loader for library services 12 | */ 13 | public final class SDLinkPlatform { 14 | 15 | public static SDLinkMinecraftBridge minecraftHelper = new SDLinkMinecraftBridge(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/networking/MentionsSyncPacket.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.networking; 2 | 3 | import com.hypherionmc.craterlib.client.mentions.MentionsController; 4 | import com.hypherionmc.craterlib.core.networking.data.PacketContext; 5 | import com.hypherionmc.craterlib.core.networking.data.PacketSide; 6 | import com.hypherionmc.craterlib.nojang.nbt.BridgedCompoundTag; 7 | import com.hypherionmc.craterlib.nojang.network.BridgedFriendlyByteBuf; 8 | import com.hypherionmc.craterlib.nojang.resources.ResourceIdentifier; 9 | import com.hypherionmc.sdlink.SDLinkConstants; 10 | import com.hypherionmc.sdlink.client.ClientEvents; 11 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 12 | 13 | import java.util.HashMap; 14 | 15 | /** 16 | * @author HypherionSA 17 | * Config Packet to send cache data from Server to Client to allow mentions 18 | */ 19 | public final class MentionsSyncPacket { 20 | 21 | public static final ResourceIdentifier CHANNEL = new ResourceIdentifier(SDLinkConstants.MOD_ID, "syncpacket"); 22 | 23 | private HashMap roles; 24 | private HashMap channelHashMap; 25 | private HashMap users; 26 | private boolean mentionsEnabled = false; 27 | 28 | public MentionsSyncPacket() {} 29 | 30 | public MentionsSyncPacket(HashMap roles, HashMap channels, HashMap users) { 31 | this.roles = roles; 32 | this.channelHashMap = channels; 33 | this.users = users; 34 | } 35 | 36 | public static MentionsSyncPacket decode(BridgedFriendlyByteBuf buf) { 37 | MentionsSyncPacket p = new MentionsSyncPacket(); 38 | 39 | BridgedCompoundTag tag = buf.readNbt(); 40 | if (tag == null) 41 | return p; 42 | 43 | BridgedCompoundTag rolesTag = tag.getCompound("roles"); 44 | BridgedCompoundTag channelsTag = tag.getCompound("channels"); 45 | BridgedCompoundTag usersTag = tag.getCompound("users"); 46 | 47 | p.roles = new HashMap<>(); 48 | rolesTag.getAllKeys().forEach(k -> p.roles.put(k, rolesTag.getString(k))); 49 | 50 | p.channelHashMap = new HashMap<>(); 51 | channelsTag.getAllKeys().forEach(k -> p.channelHashMap.put(k, channelsTag.getString(k))); 52 | 53 | p.users = new HashMap<>(); 54 | usersTag.getAllKeys().forEach(k -> p.users.put(k, usersTag.getString(k))); 55 | 56 | p.mentionsEnabled = tag.getBoolean("mentionsenabled"); 57 | 58 | return p; 59 | } 60 | 61 | public void write(BridgedFriendlyByteBuf friendlyByteBuf) { 62 | BridgedCompoundTag tag = BridgedCompoundTag.empty(); 63 | BridgedCompoundTag rolesTag = BridgedCompoundTag.empty(); 64 | BridgedCompoundTag channelsTag = BridgedCompoundTag.empty(); 65 | BridgedCompoundTag usersTag = BridgedCompoundTag.empty(); 66 | roles.forEach(rolesTag::putString); 67 | channelHashMap.forEach(channelsTag::putString); 68 | users.forEach(usersTag::putString); 69 | 70 | tag.put("roles", rolesTag); 71 | tag.put("channels", channelsTag); 72 | tag.put("users", usersTag); 73 | tag.putBoolean("mentionsenabled", SDLinkConfig.INSTANCE.chatConfig.allowMentionsFromChat); 74 | friendlyByteBuf.writeNbt(tag); 75 | } 76 | 77 | public static void handle(PacketContext ctx) { 78 | if (PacketSide.CLIENT.equals(ctx.side())) { 79 | MentionsSyncPacket p = ctx.message(); 80 | 81 | if (!(p.roles == null || p.roles.isEmpty())) { 82 | ResourceIdentifier rrl = new ResourceIdentifier("sdlink:roles"); 83 | MentionsController.registerMention(rrl, p.roles.keySet(), currentWord -> currentWord.startsWith("[@") || currentWord.startsWith("@")); 84 | } 85 | 86 | if (!(p.channelHashMap == null || p.channelHashMap.isEmpty())) { 87 | ResourceIdentifier crl = new ResourceIdentifier("sdlink:channels"); 88 | MentionsController.registerMention(crl, p.channelHashMap.keySet(), currentWord -> currentWord.startsWith("[#") || currentWord.startsWith("#")); 89 | } 90 | 91 | if (!(p.users == null || p.users.isEmpty())) { 92 | ResourceIdentifier url = new ResourceIdentifier("sdlink:users"); 93 | MentionsController.registerMention(url, p.users.keySet(), currentWord -> currentWord.startsWith("[@") || currentWord.startsWith("@")); 94 | } 95 | 96 | ClientEvents.mentionsEnabled = p.mentionsEnabled; 97 | } 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/networking/SDLinkNetworking.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.networking; 2 | 3 | import com.hypherionmc.craterlib.core.networking.CraterPacketNetwork; 4 | 5 | /** 6 | * @author HypherionSA 7 | * Network Controller 8 | */ 9 | public final class SDLinkNetworking { 10 | 11 | public static void registerPackets() { 12 | CraterPacketNetwork.registerPacket( 13 | MentionsSyncPacket.CHANNEL, 14 | MentionsSyncPacket.class, 15 | MentionsSyncPacket::write, 16 | MentionsSyncPacket::decode, 17 | MentionsSyncPacket::handle 18 | ); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/platform/SDLinkFakePlayer.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.platform; 2 | 3 | import com.hypherionmc.craterlib.nojang.commands.BridgedFakePlayer; 4 | import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; 5 | import com.hypherionmc.craterlib.utils.ChatUtils; 6 | import com.hypherionmc.sdlink.api.messaging.Result; 7 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 8 | import com.hypherionmc.sdlink.util.translations.Text; 9 | import shadow.kyori.adventure.text.Component; 10 | 11 | import java.util.concurrent.CompletableFuture; 12 | import java.util.function.Supplier; 13 | 14 | public final class SDLinkFakePlayer extends BridgedFakePlayer { 15 | 16 | private final CompletableFuture replier; 17 | 18 | public SDLinkFakePlayer(BridgedMinecraftServer server, int perm, String name, CompletableFuture replier) { 19 | super(server, perm, name); 20 | this.replier = replier; 21 | } 22 | 23 | @Override 24 | public void onSuccess(Supplier supplier, Boolean aBoolean) { 25 | try { 26 | String msg = ChatUtils.resolve(supplier.get(), SDLinkConfig.INSTANCE.chatConfig.formatting); 27 | replier.complete(Result.success(msg)); 28 | } catch (Exception e) { 29 | replier.complete(Result.error(Text.translate("error.mc_command_failed", e.getMessage()))); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/platform/SDLinkMCPlatform.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.platform; 2 | 3 | import com.hypherionmc.craterlib.core.platform.CompatUtils; 4 | import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; 5 | import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; 6 | import com.hypherionmc.craterlib.nojang.world.entity.player.BridgedPlayer; 7 | import com.hypherionmc.sdlink.api.messaging.Result; 8 | import com.hypherionmc.sdlink.core.config.SDLinkCompatConfig; 9 | import com.hypherionmc.sdlink.server.ServerEvents; 10 | import shadow.kyori.adventure.text.Component; 11 | 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | public final class SDLinkMCPlatform { 15 | 16 | public static final SDLinkMCPlatform INSTANCE = new SDLinkMCPlatform(); 17 | 18 | public void executeCommand(String command, int permLevel, String member, CompletableFuture replier) { 19 | BridgedMinecraftServer server = ServerEvents.getInstance().getMinecraftServer(); 20 | SDLinkFakePlayer fakePlayer = new SDLinkFakePlayer(server, permLevel, member, replier); 21 | 22 | try { 23 | server.executeCommand(server, fakePlayer, command); 24 | } catch (Exception e) { 25 | fakePlayer.onError(Component.text(e.getMessage())); 26 | } 27 | } 28 | 29 | public boolean isDevEnv() { 30 | return ModloaderEnvironment.INSTANCE.isDevEnv(); 31 | } 32 | 33 | public String getPlayerSkinUUID(BridgedPlayer player) { 34 | return CompatUtils.INSTANCE.getSkinUUID(player); 35 | } 36 | 37 | public boolean playerIsActive(BridgedPlayer player) { 38 | if (!SDLinkCompatConfig.INSTANCE.common.vanish) { 39 | return true; 40 | } 41 | 42 | return CompatUtils.INSTANCE.isPlayerActive(player); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/server/commands/ConfigEditorCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.server.commands; 2 | 3 | import com.hypherionmc.craterlib.api.commands.CraterCommand; 4 | import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; 5 | import com.hypherionmc.sdlink.core.editor.ConfigEditorClient; 6 | import com.hypherionmc.sdlink.util.translations.Text; 7 | import shadow.kyori.adventure.text.Component; 8 | 9 | public final class ConfigEditorCommand { 10 | 11 | public static void register(CraterRegisterCommandEvent event) { 12 | CraterCommand cmd = CraterCommand.literal("sdconfigeditor") 13 | .requiresPermission(4) 14 | .withNode("sdlink.configeditor") 15 | .execute(ctx -> { 16 | ConfigEditorClient.INSTANCE.openConnection(ctx); 17 | ctx.sendSuccess(() -> Component.text(Text.translate("mc.sdconfigeditor.opening").toString()), false); 18 | return 1; 19 | }); 20 | 21 | event.registerCommand(cmd); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/server/commands/DiscordCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.server.commands; 2 | 3 | import com.hypherionmc.craterlib.api.commands.CraterCommand; 4 | import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; 5 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 6 | import com.hypherionmc.sdlink.util.SDLinkChatUtils; 7 | 8 | public final class DiscordCommand { 9 | 10 | public static void register(CraterRegisterCommandEvent event) { 11 | CraterCommand cmd = CraterCommand.literal("discord") 12 | .requiresPermission(0) 13 | .withNode("sdlink.discordinvite") 14 | .execute(ctx -> { 15 | if (SDLinkConfig.INSTANCE.botConfig.invite.inviteLink != null && !SDLinkConfig.INSTANCE.botConfig.invite.inviteLink.isEmpty()) { 16 | 17 | String invite = SDLinkConfig.INSTANCE.botConfig.invite.inviteMessage 18 | .replace("%inviteurl%", SDLinkConfig.INSTANCE.botConfig.invite.inviteLink); 19 | 20 | ctx.sendSuccess(() -> SDLinkChatUtils.parseChatLinks(invite), false); 21 | } 22 | 23 | return 1; 24 | }); 25 | 26 | event.registerCommand(cmd); 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/server/commands/DiscordVerifyCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.server.commands; 2 | 3 | import com.hypherionmc.craterlib.api.commands.CraterCommand; 4 | import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; 5 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 6 | import com.hypherionmc.sdlink.core.config.SDLinkConfig; 7 | import com.hypherionmc.sdlink.core.database.SDLinkAccount; 8 | import com.hypherionmc.sdlink.core.managers.DatabaseManager; 9 | import com.hypherionmc.sdlink.util.SDLinkUtils; 10 | import com.hypherionmc.sdlink.util.translations.Text; 11 | import shadow.kyori.adventure.text.Component; 12 | 13 | public final class DiscordVerifyCommand { 14 | 15 | public static void register(CraterRegisterCommandEvent event) { 16 | CraterCommand cmd = CraterCommand.literal("discordverify") 17 | .requiresPermission(0) 18 | .withNode("sdlink.discord_verify") 19 | .execute(ctx -> { 20 | if (!ctx.isPlayer() || ctx.getPlayer() == null) { 21 | ctx.sendFailure(Component.text(Text.translate("error.verify.only_by_players").toString())); 22 | return 1; 23 | } 24 | 25 | if (!SDLinkConfig.INSTANCE.accessControl.enabled && !SDLinkConfig.INSTANCE.accessControl.optionalVerification) { 26 | ctx.sendFailure(Component.text(Text.translate("error.verify.not_enabled").toString())); 27 | return 1; 28 | } 29 | 30 | MinecraftAccount account = MinecraftAccount.of(ctx.getPlayer().getGameProfile()); 31 | SDLinkAccount sdLinkAccount = account.getStoredAccount(); 32 | 33 | if (sdLinkAccount == null) { 34 | ctx.sendFailure(Component.text(Text.translate("account.load_failed").toString())); 35 | return 1; 36 | } 37 | 38 | if (SDLinkUtils.isNullOrEmpty(sdLinkAccount.getVerifyCode())) { 39 | int code = SDLinkUtils.intInRange(1000, 9999); 40 | sdLinkAccount.setVerifyCode(String.valueOf(code)); 41 | DatabaseManager.INSTANCE.updateEntry(sdLinkAccount); 42 | ctx.sendSuccess(() -> Component.text(SDLinkConfig.INSTANCE.accessControl.verificationMessages.optionalVerificationMessage.replace("{code}", String.valueOf(code))), false); 43 | } else { 44 | ctx.sendSuccess(() -> Component.text(SDLinkConfig.INSTANCE.accessControl.verificationMessages.optionalVerificationMessage.replace("{code}", String.valueOf(sdLinkAccount.getVerifyCode()))), false); 45 | } 46 | return 1; 47 | }); 48 | 49 | event.registerCommand(cmd); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/server/commands/HidePlayerCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.server.commands; 2 | 3 | import com.hypherionmc.craterlib.api.commands.CraterCommand; 4 | import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; 5 | import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; 6 | import com.hypherionmc.sdlink.api.messaging.Result; 7 | import com.hypherionmc.sdlink.core.managers.HiddenPlayersManager; 8 | import com.hypherionmc.sdlink.util.translations.Text; 9 | import shadow.kyori.adventure.text.Component; 10 | 11 | public final class HidePlayerCommand { 12 | 13 | public static void register(CraterRegisterCommandEvent event) { 14 | CraterCommand command = CraterCommand.literal("hideplayer") 15 | .requiresPermission(4) 16 | .withNode("sdlink.muteplayer") 17 | .withGameProfilesArgument("username", (player, profiles, ctx) -> { 18 | if (profiles.isEmpty()) { 19 | ctx.sendSuccess(() -> Component.text(Text.translate("error.hiding.no_account_supplied").toString()), true); 20 | return 1; 21 | } 22 | 23 | BridgedGameProfile profile = profiles.get(0); 24 | Result result = HiddenPlayersManager.INSTANCE.hidePlayer(profile.getId().toString(), profile.getName(), "minecraft"); 25 | ctx.sendSuccess(() -> Component.text(result.getMessage()), true); 26 | return 1; 27 | }); 28 | 29 | event.registerCommand(command); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/server/commands/ReloadBotCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.server.commands; 2 | 3 | import com.hypherionmc.craterlib.api.commands.CraterCommand; 4 | import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; 5 | import com.hypherionmc.sdlink.server.ServerEvents; 6 | import com.hypherionmc.sdlink.util.translations.Text; 7 | import shadow.kyori.adventure.text.Component; 8 | 9 | public final class ReloadBotCommand { 10 | 11 | public static void register(CraterRegisterCommandEvent event) { 12 | CraterCommand cmd = CraterCommand.literal("reloadbot") 13 | .requiresPermission(4) 14 | .withNode("sdlink.reloadbot") 15 | .execute(ctx -> { 16 | ServerEvents.reloadBot(true); 17 | ctx.sendSuccess(() -> Component.text(Text.translate("feedback.bot_reloaded").toString()), true); 18 | return 1; 19 | }); 20 | 21 | event.registerCommand(cmd); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/server/commands/ReloadEmbedsCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.server.commands; 2 | 3 | import com.hypherionmc.craterlib.api.commands.CraterCommand; 4 | import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; 5 | import com.hypherionmc.sdlink.core.managers.EmbedManager; 6 | import shadow.kyori.adventure.text.Component; 7 | 8 | public final class ReloadEmbedsCommand { 9 | 10 | public static void register(CraterRegisterCommandEvent event) { 11 | CraterCommand cmd = CraterCommand.literal("reloadembeds") 12 | .requiresPermission(4) 13 | .withNode("sdlink.reloadembeds") 14 | .execute(ctx -> { 15 | EmbedManager.init(); 16 | ctx.sendSuccess(() -> Component.text("Reloaded Embeds"), false); 17 | return 1; 18 | }); 19 | 20 | event.registerCommand(cmd); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/server/commands/UnhidePlayerCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.server.commands; 2 | 3 | import com.hypherionmc.craterlib.api.commands.CraterCommand; 4 | import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; 5 | import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile; 6 | import com.hypherionmc.sdlink.api.messaging.Result; 7 | import com.hypherionmc.sdlink.core.managers.HiddenPlayersManager; 8 | import com.hypherionmc.sdlink.util.translations.Text; 9 | import shadow.kyori.adventure.text.Component; 10 | 11 | public final class UnhidePlayerCommand { 12 | 13 | public static void register(CraterRegisterCommandEvent event) { 14 | CraterCommand command = CraterCommand.literal("unhideplayer") 15 | .requiresPermission(4) 16 | .withNode("sdlink.unmuteplayer") 17 | .withGameProfilesArgument("username", (player, profiles, ctx) -> { 18 | if (profiles.isEmpty()) { 19 | ctx.sendSuccess(() -> Component.text(Text.translate("error.hiding.unhide_no_account_provided").toString()), true); 20 | return 1; 21 | } 22 | 23 | BridgedGameProfile profile = profiles.get(0); 24 | Result result = HiddenPlayersManager.INSTANCE.unhidePlayer(profile.getId().toString()); 25 | ctx.sendSuccess(() -> Component.text(result.getMessage()), true); 26 | return 1; 27 | }); 28 | 29 | event.registerCommand(command); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/server/commands/WhoisCommand.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.server.commands; 2 | 3 | import com.hypherionmc.craterlib.api.commands.CraterCommand; 4 | import com.hypherionmc.craterlib.api.events.server.CraterRegisterCommandEvent; 5 | import com.hypherionmc.sdlink.api.accounts.MinecraftAccount; 6 | import com.hypherionmc.sdlink.core.discord.BotController; 7 | import com.hypherionmc.sdlink.util.translations.Text; 8 | import shadow.kyori.adventure.text.Component; 9 | 10 | public final class WhoisCommand { 11 | 12 | public static void register(CraterRegisterCommandEvent event) { 13 | CraterCommand cmd = CraterCommand.literal("whois") 14 | .requiresPermission(4) 15 | .withNode("sdlink.whois") 16 | .withGameProfilesArgument("username", (player, profiles, ctx) -> { 17 | if (BotController.INSTANCE != null) { 18 | if (profiles.isEmpty()) { 19 | ctx.sendSuccess(() -> Component.text(Text.translate("account.unlinked").toString()), true); 20 | return 1; 21 | } 22 | 23 | MinecraftAccount account = MinecraftAccount.of(profiles.stream().findFirst().get()); 24 | String value = account.isAccountVerified() ? account.getDiscordName() : Text.translate("account.unlinked").toString(); 25 | ctx.sendSuccess(() -> Component.text(value), true); 26 | } 27 | return 1; 28 | }); 29 | 30 | event.registerCommand(cmd); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/util/DestinationHolder.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.util; 2 | 3 | import club.minnced.discord.webhook.WebhookClient; 4 | import com.hypherionmc.sdlink.api.messaging.MessageDestination; 5 | import com.hypherionmc.sdlink.api.messaging.MessageType; 6 | import com.hypherionmc.sdlink.core.config.impl.MessageChannelConfig; 7 | import com.hypherionmc.sdlink.core.managers.ChannelManager; 8 | import com.hypherionmc.sdlink.core.managers.WebhookManager; 9 | import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public final class DestinationHolder { 13 | 14 | private final GuildMessageChannel channel; 15 | private final WebhookClient webhookClient; 16 | private final MessageChannelConfig.DestinationObject destination; 17 | 18 | private DestinationHolder(MessageChannelConfig.DestinationObject destination, MessageType type) { 19 | 20 | if (ChannelManager.getOverride(type) != null) { 21 | this.channel = ChannelManager.getOverride(type); 22 | } else { 23 | this.channel = ChannelManager.getDestinationChannel(destination.channel); 24 | } 25 | 26 | if (WebhookManager.getOverride(type) != null) { 27 | this.webhookClient = WebhookManager.getOverride(type); 28 | } else { 29 | this.webhookClient = WebhookManager.getWebhookClient(destination.channel); 30 | } 31 | 32 | this.destination = destination; 33 | } 34 | 35 | public static DestinationHolder of(MessageChannelConfig.DestinationObject destination, MessageType type) { 36 | return new DestinationHolder(destination, type); 37 | } 38 | 39 | @Nullable 40 | public GuildMessageChannel channel() { 41 | return channel; 42 | } 43 | 44 | @Nullable 45 | public WebhookClient webhook() { 46 | return webhookClient; 47 | } 48 | 49 | public MessageDestination destination() { 50 | return destination.channel; 51 | } 52 | 53 | public boolean useEmbed() { 54 | return destination.useEmbed; 55 | } 56 | 57 | public String embedLayout() { 58 | return destination.embedLayout; 59 | } 60 | 61 | public boolean hasWebhook() { 62 | return this.webhookClient != null; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/util/EncryptionUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.util; 6 | 7 | import com.hypherionmc.sdlink.core.discord.BotController; 8 | import org.apache.commons.io.FileUtils; 9 | import org.apache.commons.lang3.RandomStringUtils; 10 | import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; 11 | import org.jasypt.exceptions.EncryptionOperationNotPossibleException; 12 | 13 | import java.io.File; 14 | import java.nio.charset.StandardCharsets; 15 | 16 | /** 17 | * @author HypherionSA 18 | * Util Class to handle Encryption/Decryption of Bot-Tokens and Webhook URLS 19 | * Since people DON'T READ THE COMMENTS and leave these in-tact, 20 | * they are now encrypted by default 21 | */ 22 | public final class EncryptionUtil { 23 | 24 | private final boolean canRun; 25 | public static EncryptionUtil INSTANCE = getInstance(); 26 | // Instance of the Encryptor Used 27 | private final StandardPBEStringEncryptor encryptor; 28 | 29 | public EncryptionUtil(String encCode) { 30 | canRun = !encCode.isEmpty(); 31 | 32 | encryptor = new StandardPBEStringEncryptor(); 33 | if (canRun) 34 | encryptor.setPassword(encCode); 35 | 36 | if (!canRun) 37 | BotController.INSTANCE.getLogger().error("Failed to initialize encryption system. Your config values will not be encrypted!"); 38 | } 39 | 40 | private static EncryptionUtil getInstance() { 41 | if (INSTANCE == null) { 42 | String encCode = ""; 43 | 44 | File storageDir = new File("sdlinkstorage"); 45 | if (storageDir.exists()) 46 | storageDir.mkdirs(); 47 | 48 | // Try to read a saved encryption key, or try to save a new one 49 | try { 50 | File encKey = new File(storageDir.getAbsolutePath() + File.separator + "sdlink.enc"); 51 | if (!encKey.exists()) { 52 | FileUtils.writeStringToFile(encKey, getSaltString(), StandardCharsets.UTF_8); 53 | } 54 | encCode = FileUtils.readFileToString(encKey, StandardCharsets.UTF_8); 55 | } catch (Exception e) { 56 | BotController.INSTANCE.getLogger().error("Failed to initialize Encryption", e); 57 | } 58 | 59 | INSTANCE = new EncryptionUtil(encCode); 60 | } 61 | return INSTANCE; 62 | } 63 | 64 | /** 65 | * Will Encrypt the string passed into it, if it's not already encrypted 66 | * 67 | * @param input The string to be encrypted 68 | * @return The encrypted string 69 | */ 70 | public String encrypt(String input) { 71 | if (!canRun) 72 | return input; 73 | 74 | if (isEncrypted(input)) { 75 | return input; 76 | } 77 | 78 | input = "enc:" + input; 79 | return encryptor.encrypt(input); 80 | } 81 | 82 | /** 83 | * Decrypts an encrypted string 84 | * 85 | * @param input The encrypted String 86 | * @return The Plain Text String 87 | */ 88 | public String decrypt(String input) { 89 | if (!canRun) 90 | return input; 91 | 92 | if (!isEncrypted(input)) 93 | return input; 94 | 95 | input = internalDecrypt(input); 96 | 97 | if (input.startsWith("enc:")) { 98 | input = input.replaceFirst("enc:", ""); 99 | } 100 | return input; 101 | } 102 | 103 | // Used internally 104 | private String internalDecrypt(String input) { 105 | if (!canRun) 106 | return input; 107 | 108 | return encryptor.decrypt(input); 109 | } 110 | 111 | // Test if String is encrypted 112 | private boolean isEncrypted(String input) { 113 | try { 114 | String temp = internalDecrypt(input); 115 | return temp.startsWith("enc:"); 116 | } catch (EncryptionOperationNotPossibleException ignored) { 117 | // String is likely not encrypted 118 | } 119 | return false; 120 | } 121 | 122 | // Generate Random codes for encryption/decryption 123 | public static String getSaltString() { 124 | return RandomStringUtils.random(SDLinkUtils.intInRange(30, 100), true, true); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/util/MessageUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.util; 6 | 7 | import com.hypherionmc.sdlink.core.discord.BotController; 8 | import com.jagrosh.jdautilities.menu.ButtonEmbedPaginator; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.TimeUnit; 12 | import java.util.stream.IntStream; 13 | import java.util.stream.Stream; 14 | 15 | /** 16 | * @author HypherionSA 17 | * Util classes to help manage certain discord message actions 18 | */ 19 | public final class MessageUtil { 20 | 21 | /** 22 | * Create an Embed Paginator for use with Slash Commands 23 | */ 24 | public static ButtonEmbedPaginator.Builder defaultPaginator() { 25 | return new ButtonEmbedPaginator.Builder() 26 | .setTimeout(1, TimeUnit.MINUTES) 27 | .setEventWaiter(BotController.INSTANCE.getEventWaiter()) 28 | .waitOnSinglePage(false) 29 | .setFinalAction(m -> m.editMessageComponents().queue()); 30 | } 31 | 32 | /** 33 | * Split a large list of items into smaller sublists. This is to help with Discord limits on pagination 34 | * 35 | * @param source The list of objects to split 36 | * @param length How many entries are allowed per sub-list 37 | */ 38 | public static Stream> listBatches(List source, int length) { 39 | if (length <= 0) 40 | throw new IllegalArgumentException("length = " + length); 41 | int size = source.size(); 42 | if (size <= 0) 43 | return Stream.empty(); 44 | int fullChunks = (size - 1) / length; 45 | return IntStream.range(0, fullChunks + 1).mapToObj( 46 | n -> source.subList(n * length, n == fullChunks ? size : (n + 1) * length)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/util/PKUtil.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.util; 2 | 3 | import com.hypherionmc.sdlink.core.discord.BotController; 4 | import net.dv8tion.jda.api.events.message.MessageReceivedEvent; 5 | import org.jetbrains.annotations.Nullable; 6 | import org.json.JSONObject; 7 | 8 | import java.io.IOException; 9 | import java.net.HttpURLConnection; 10 | import java.net.URI; 11 | import java.net.http.HttpClient; 12 | import java.net.http.HttpRequest; 13 | import java.net.http.HttpResponse; 14 | import java.time.Duration; 15 | import java.util.Collections; 16 | import java.util.HashSet; 17 | import java.util.Objects; 18 | import java.util.Set; 19 | 20 | public class PKUtil { 21 | public static final String PK_APP_ID = "466378653216014359"; 22 | public static final Set PK_USERS = Collections.synchronizedSet(new HashSet<>()); 23 | 24 | public static boolean isPK(MessageReceivedEvent event) { 25 | if (event.isWebhookMessage()) { 26 | if (Objects.equals(event.getMessage().getApplicationId(), PK_APP_ID)){ 27 | new Thread(() -> { 28 | String sender = PKUtil.getSender(event.getMessageId()); 29 | if (sender != null) PK_USERS.add(sender); 30 | }).start(); 31 | return true; 32 | } 33 | return false; 34 | } 35 | if (PK_USERS.contains(event.getAuthor().getId())) { 36 | return getSender(event.getMessageId()) != null; 37 | } 38 | return false; 39 | } 40 | 41 | @Nullable 42 | public static String getSender(String id) { 43 | try { 44 | HttpClient client = HttpClient.newBuilder() 45 | .connectTimeout(Duration.ofMillis(1_000)) // Set a one-second timeout to avoid indefinitely blocking the thread 46 | .build(); 47 | HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://api.pluralkit.me/v2/messages/" + id)).build(); 48 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 49 | 50 | 51 | if (response.statusCode() != HttpURLConnection.HTTP_OK) { 52 | return null; 53 | } 54 | 55 | JSONObject json = new JSONObject(response.body()); 56 | 57 | return json.getString("sender"); 58 | } catch (IOException | InterruptedException e) { 59 | BotController.INSTANCE.getLogger().error("An error occurred while checking message in PluralKit api", e); 60 | return null; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/util/SDLinkUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.util; 6 | 7 | public final class SDLinkUtils { 8 | 9 | public static boolean isNullOrEmpty(String inString) { 10 | return inString == null || inString.trim().isEmpty(); 11 | } 12 | 13 | public static int intInRange(int min, int max) { 14 | return (int) ((Math.random() * (max - min)) + min); 15 | } 16 | 17 | public static String getOrElse(String inString, String defaultValue) { 18 | return isNullOrEmpty(inString) ? defaultValue : inString; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/util/SystemUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.util; 6 | 7 | import java.text.CharacterIterator; 8 | import java.text.StringCharacterIterator; 9 | 10 | public final class SystemUtils { 11 | 12 | /** 13 | * Convert Bytes into a human-readable format, like 1GB 14 | * From https://stackoverflow.com/a/3758880 15 | * 16 | * @param bytes The Size in Bytes 17 | * @return The size formatted in KB, MB, GB, TB, PB etc 18 | */ 19 | public static String byteToHuman(long bytes) { 20 | long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); 21 | if (absB < 1024) { 22 | return bytes + " B"; 23 | } 24 | long value = absB; 25 | CharacterIterator ci = new StringCharacterIterator("KMGTPE"); 26 | for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { 27 | value >>= 10; 28 | ci.next(); 29 | } 30 | value *= Long.signum(bytes); 31 | return String.format("%.1f %ciB", value / 1024.0, ci.current()); 32 | } 33 | 34 | // Time Conversion 35 | 36 | /** 37 | * Convert Seconds into a Timestamp 38 | * 39 | * @param sec Input in seconds 40 | */ 41 | public static String secondsToTimestamp(long sec) { 42 | long seconds = sec % 60; 43 | long minutes = (sec / 60) % 60; 44 | long hours = (sec / 3600) % 24; 45 | long days = sec / (3600 * 24); 46 | 47 | String timeString = String.format("%02d hour(s), %02d minute(s), %02d second(s)", hours, minutes, seconds); 48 | 49 | if (days > 0) { 50 | timeString = String.format("%d day(s), %s", days, timeString); 51 | } 52 | 53 | return timeString; 54 | } 55 | 56 | public static boolean isLong(String input) { 57 | try { 58 | Long.parseLong(input); 59 | return true; 60 | } catch (NumberFormatException ignored) { 61 | } 62 | return false; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/util/ThreadedEventManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of sdlink-core, licensed under the MIT License (MIT). 3 | * Copyright HypherionSA and Contributors 4 | */ 5 | package com.hypherionmc.sdlink.util; 6 | 7 | import com.hypherionmc.sdlink.core.discord.BotController; 8 | import net.dv8tion.jda.api.events.GenericEvent; 9 | import net.dv8tion.jda.api.hooks.InterfacedEventManager; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | /** 15 | * @author HypherionSA 16 | * Run discord events in seperate threads 17 | */ 18 | public final class ThreadedEventManager extends InterfacedEventManager { 19 | 20 | @Override 21 | public void handle(@NotNull GenericEvent event) { 22 | try { 23 | if (BotController.INSTANCE.taskManager.isShutdown() || BotController.INSTANCE.taskManager.isTerminated()) 24 | return; 25 | 26 | CompletableFuture.runAsync(() -> super.handle(event), BotController.INSTANCE.taskManager); 27 | } catch (Exception ignored) {} 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/util/configeditor/SocketResponse.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.util.configeditor; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @AllArgsConstructor(staticName = "of") 7 | @Getter 8 | public final class SocketResponse { 9 | 10 | private String socketCode; 11 | private String message; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Common/src/main/java/com/hypherionmc/sdlink/util/translations/Text.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.util.translations; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | /** 7 | * @author HypherionSA 8 | * Extended Helper Class to help resolve translation keys into Strings 9 | */ 10 | public final class Text implements CharSequence { 11 | 12 | private final String translatedText; 13 | 14 | private Text(String key, @Nullable Object... args) { 15 | this.translatedText = (args == null || args.length == 0) ? 16 | TranslationManager.INSTANCE.translate(key) 17 | : String.format(TranslationManager.INSTANCE.translate(key), args); 18 | } 19 | 20 | /** 21 | * Load a translation key into a string 22 | * 23 | * @param key The Translation key to load 24 | * @return The final text, or the translation key if the text does not exist 25 | */ 26 | public static Text translate(@NotNull String key) { 27 | return new Text(key); 28 | } 29 | 30 | /** 31 | * Load a translation key into a string 32 | * 33 | * @param key The Translation key to load 34 | * @param args List of objects that will replace placeholders in translated text 35 | * @return The final text, or the translation key if the text does not exist 36 | */ 37 | public static Text translate(@NotNull String key, @NotNull Object... args) { 38 | return new Text(key, args); 39 | } 40 | 41 | @Override 42 | public int length() { 43 | return translatedText.length(); 44 | } 45 | 46 | @Override 47 | public char charAt(int index) { 48 | return translatedText.charAt(index); 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public CharSequence subSequence(int start, int end) { 54 | return translatedText.subSequence(start, end); 55 | } 56 | 57 | @NotNull 58 | @Override 59 | public String toString() { 60 | return translatedText; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Common/src/main/resources/assets/sdlink/lang/zh_tw.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypherionmc/sdlink/ae429d457ad1f89745f9be9d393188b4535782c4/Common/src/main/resources/assets/sdlink/lang/zh_tw.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-2025 HypherionSA and Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /ModLoaders/build.gradle: -------------------------------------------------------------------------------- 1 | import com.hypherionmc.modpublisher.properties.ReleaseType 2 | archivesBaseName = "${mod_name.replace(" ", "")}-Universal" 3 | 4 | dependencies { 5 | // Do not edit or remove 6 | implementation project(":Common") 7 | } 8 | 9 | publisher { 10 | apiKeys { 11 | modrinth(System.getenv("MODRINTH_TOKEN")) 12 | curseforge(System.getenv("CURSE_TOKEN")) 13 | nightbloom(System.getenv("PLATFORM_KEY")) 14 | } 15 | 16 | setCurseID(curse_id) 17 | setModrinthID(modrinth_id) 18 | setNightbloomID("sdlink") 19 | setProjectVersion(project.version.toString()) 20 | setChangelog(project.rootProject.file("changelog.md")) 21 | setReleaseType(ReleaseType.RELEASE) 22 | setDisplayName("[1.18.2 - 1.21.10] Simple Discord Link Universal - ${project.version}") 23 | setGameVersions("1.18.2", "1.19.2", "1.19.3", "1.19.4", "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4", "1.21", "1.21.1", "1.21.3", "1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10") 24 | setLoaders("forge", "fabric", "quilt", "neoforge") 25 | setCurseEnvironment("both") 26 | setArtifact(shadowJar) 27 | 28 | modrinthDepends { 29 | required("Nn8Wasaq") 30 | } 31 | 32 | curseDepends { 33 | required("craterlib") 34 | } 35 | 36 | nightbloomDepends { 37 | required("craterlib") 38 | } 39 | } 40 | 41 | publishing { 42 | publications { 43 | mavenJava(MavenPublication) { 44 | group "com.hypherionmc.sdlink" 45 | artifactId "sdlink" 46 | 47 | artifact(shadowJar) { 48 | builtBy shadowJar 49 | } 50 | artifact sourcesJar 51 | artifact javadocJar 52 | } 53 | } 54 | 55 | repositories { 56 | maven orion.getPublishingMaven() 57 | } 58 | } -------------------------------------------------------------------------------- /ModLoaders/src/main/java/com/hypherionmc/sdlink/loaders/fabric/SDLinkFabric.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.loaders.fabric; 2 | 3 | import com.hypherionmc.craterlib.core.event.CraterEventBus; 4 | import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; 5 | import com.hypherionmc.sdlink.compat.MModeCompat; 6 | import com.hypherionmc.sdlink.compat.rolesync.RoleSync; 7 | import com.hypherionmc.sdlink.compat.rolesync.impl.FTBRankSync; 8 | import com.hypherionmc.sdlink.compat.rolesync.impl.LuckPermsSync; 9 | import com.hypherionmc.sdlink.compat.rolesync.impl.PlayerRolesSync; 10 | import com.hypherionmc.sdlink.networking.SDLinkNetworking; 11 | import com.hypherionmc.sdlink.server.ServerEvents; 12 | import net.fabricmc.api.DedicatedServerModInitializer; 13 | 14 | public final class SDLinkFabric implements DedicatedServerModInitializer { 15 | 16 | @Override 17 | public void onInitializeServer() { 18 | ServerEvents events = ServerEvents.getInstance(); 19 | CraterEventBus.INSTANCE.registerEventListener(events); 20 | SDLinkNetworking.registerPackets(); 21 | 22 | if (ModloaderEnvironment.INSTANCE.isModLoaded("mmode")) { 23 | MModeCompat.init(); 24 | } 25 | 26 | if (ModloaderEnvironment.INSTANCE.isModLoaded("ftbranks")) { 27 | CraterEventBus.INSTANCE.registerEventListener(FTBRankSync.INSTANCE); 28 | } 29 | 30 | if (ModloaderEnvironment.INSTANCE.isModLoaded("luckperms")) { 31 | CraterEventBus.INSTANCE.registerEventListener(LuckPermsSync.INSTANCE); 32 | } 33 | 34 | if (ModloaderEnvironment.INSTANCE.isModLoaded("player_roles")) { 35 | CraterEventBus.INSTANCE.registerEventListener(PlayerRolesSync.INSTANCE); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ModLoaders/src/main/java/com/hypherionmc/sdlink/loaders/fabric/SDLinkFabricClient.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.loaders.fabric; 2 | 3 | import com.hypherionmc.sdlink.client.ClientEvents; 4 | import com.hypherionmc.sdlink.networking.SDLinkNetworking; 5 | import net.fabricmc.api.ClientModInitializer; 6 | 7 | /** 8 | * @author HypherionSA 9 | * Client Initializer 10 | */ 11 | public final class SDLinkFabricClient implements ClientModInitializer { 12 | 13 | @Override 14 | public void onInitializeClient() { 15 | ClientEvents.init(); 16 | SDLinkNetworking.registerPackets(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ModLoaders/src/main/java/com/hypherionmc/sdlink/loaders/forge/SDLinkForge.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.loaders.forge; 2 | 3 | import com.hypherionmc.craterlib.core.event.CraterEventBus; 4 | import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; 5 | import com.hypherionmc.sdlink.SDLinkConstants; 6 | import com.hypherionmc.sdlink.client.ClientEvents; 7 | import com.hypherionmc.sdlink.compat.MModeCompat; 8 | import com.hypherionmc.sdlink.compat.rolesync.RoleSync; 9 | import com.hypherionmc.sdlink.compat.rolesync.impl.FTBRankSync; 10 | import com.hypherionmc.sdlink.compat.rolesync.impl.LuckPermsSync; 11 | import com.hypherionmc.sdlink.networking.SDLinkNetworking; 12 | import com.hypherionmc.sdlink.server.ServerEvents; 13 | import net.minecraftforge.api.distmarker.Dist; 14 | import net.minecraftforge.fml.DistExecutor; 15 | import net.minecraftforge.fml.common.Mod; 16 | 17 | @Mod(SDLinkConstants.MOD_ID) 18 | public final class SDLinkForge { 19 | 20 | public SDLinkForge() { 21 | SDLinkNetworking.registerPackets(); 22 | 23 | DistExecutor.unsafeRunWhenOn(Dist.DEDICATED_SERVER, () -> () -> { 24 | ServerEvents events = ServerEvents.getInstance(); 25 | CraterEventBus.INSTANCE.registerEventListener(events); 26 | 27 | if (ModloaderEnvironment.INSTANCE.isModLoaded("mmode")) { 28 | MModeCompat.init(); 29 | } 30 | 31 | if (ModloaderEnvironment.INSTANCE.isModLoaded("ftbranks")) { 32 | CraterEventBus.INSTANCE.registerEventListener(FTBRankSync.INSTANCE); 33 | } 34 | 35 | if (ModloaderEnvironment.INSTANCE.isModLoaded("luckperms")) { 36 | CraterEventBus.INSTANCE.registerEventListener(LuckPermsSync.INSTANCE); 37 | } 38 | }); 39 | 40 | DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> ClientEvents::init); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ModLoaders/src/main/java/com/hypherionmc/sdlink/loaders/neoforge/SDLinkNeoForge.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.loaders.neoforge; 2 | 3 | import com.hypherionmc.craterlib.core.event.CraterEventBus; 4 | import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment; 5 | import com.hypherionmc.sdlink.SDLinkConstants; 6 | import com.hypherionmc.sdlink.client.ClientEvents; 7 | import com.hypherionmc.sdlink.compat.MModeCompat; 8 | import com.hypherionmc.sdlink.compat.rolesync.RoleSync; 9 | import com.hypherionmc.sdlink.compat.rolesync.impl.FTBRankSync; 10 | import com.hypherionmc.sdlink.compat.rolesync.impl.LuckPermsSync; 11 | import com.hypherionmc.sdlink.networking.SDLinkNetworking; 12 | import com.hypherionmc.sdlink.server.ServerEvents; 13 | import net.neoforged.bus.api.IEventBus; 14 | import net.neoforged.fml.common.Mod; 15 | 16 | @Mod(SDLinkConstants.MOD_ID) 17 | public final class SDLinkNeoForge { 18 | 19 | public SDLinkNeoForge(IEventBus bus) { 20 | SDLinkNetworking.registerPackets(); 21 | 22 | if (ModloaderEnvironment.INSTANCE.getEnvironment().isServer()) { 23 | ServerEvents events = ServerEvents.getInstance(); 24 | CraterEventBus.INSTANCE.registerEventListener(events); 25 | 26 | if (ModloaderEnvironment.INSTANCE.isModLoaded("mmode")) { 27 | MModeCompat.init(); 28 | } 29 | 30 | if (ModloaderEnvironment.INSTANCE.isModLoaded("ftbranks")) { 31 | CraterEventBus.INSTANCE.registerEventListener(FTBRankSync.INSTANCE); 32 | } 33 | 34 | if (ModloaderEnvironment.INSTANCE.isModLoaded("luckperms")) { 35 | CraterEventBus.INSTANCE.registerEventListener(LuckPermsSync.INSTANCE); 36 | } 37 | } 38 | 39 | if (ModloaderEnvironment.INSTANCE.getEnvironment().isClient()) { 40 | ClientEvents.init(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ModLoaders/src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | loaderVersion="[1,)" 3 | license="MIT" 4 | issueTrackerURL="https://github.com/firstdarkdev/sdlink/issues" 5 | 6 | [[mods]] 7 | modId="${mod_id}" 8 | version="${version}" 9 | displayName="${mod_name}" 10 | logoFile="sdlink_logo.png" 11 | authors="${mod_author}" 12 | description='''A simple Minecraft to Discord Chat bot with Whitelisting''' 13 | displayTest = "IGNORE_SERVER_VERSION" 14 | 15 | [[dependencies.${mod_id}]] 16 | modId="forge" 17 | mandatory=false 18 | versionRange="[40,)" 19 | ordering="NONE" 20 | side="BOTH" 21 | 22 | [[dependencies.${mod_id}]] 23 | modId="neoforge" 24 | mandatory=false 25 | versionRange="[20.4,)" 26 | ordering="NONE" 27 | side="BOTH" 28 | 29 | [[dependencies.${mod_id}]] 30 | modId="minecraft" 31 | mandatory=true 32 | versionRange="[1.18.2,)" 33 | ordering="NONE" 34 | side="BOTH" 35 | 36 | [[dependencies.${mod_id}]] 37 | modId="craterlib" 38 | mandatory=true 39 | versionRange="[2.1.5,)" 40 | ordering="AFTER" 41 | side="SERVER" -------------------------------------------------------------------------------- /ModLoaders/src/main/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | loaderVersion="[1,)" 3 | license="MIT" 4 | issueTrackerURL="https://github.com/firstdarkdev/sdlink/issues" 5 | 6 | [[mods]] 7 | modId="${mod_id}" 8 | version="${version}" 9 | displayName="${mod_name}" 10 | logoFile="sdlink_logo.png" 11 | authors="${mod_author}" 12 | description='''A simple Minecraft to Discord Chat bot with Whitelisting''' 13 | displayTest = "IGNORE_SERVER_VERSION" 14 | 15 | [[dependencies.${mod_id}]] 16 | modId="neoforge" 17 | type="required" 18 | versionRange="[20.4,)" 19 | ordering="NONE" 20 | side="BOTH" 21 | 22 | [[dependencies.${mod_id}]] 23 | modId="minecraft" 24 | type="required" 25 | versionRange="[1.20.4,)" 26 | ordering="NONE" 27 | side="BOTH" 28 | 29 | [[dependencies.${mod_id}]] 30 | modId="craterlib" 31 | mandatory=true 32 | versionRange="[2.1.5,)" 33 | ordering="AFTER" 34 | side="SERVER" -------------------------------------------------------------------------------- /ModLoaders/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "${mod_id}", 4 | "version": "${version}", 5 | "name": "${mod_name}", 6 | "description": "A simple Minecraft to Discord Chat bot with Whitelisting", 7 | "authors": [ 8 | "${mod_author}" 9 | ], 10 | "contact": { 11 | "homepage": "https://github.com/firstdarkdev/sdlink", 12 | "issues": "https://github.com/firstdarkdev/sdlink/issues", 13 | "discord": "https://discord.firstdark.dev" 14 | }, 15 | "license": "MIT", 16 | "environment": "*", 17 | "entrypoints": { 18 | "server": [ 19 | "com.hypherionmc.sdlink.loaders.fabric.SDLinkFabric" 20 | ], 21 | "client": [ 22 | "com.hypherionmc.sdlink.loaders.fabric.SDLinkFabricClient" 23 | ] 24 | }, 25 | "depends": { 26 | "fabricloader": ">=0.15", 27 | "fabric": "*", 28 | "minecraft": ">=1.18.2", 29 | "craterlib": ">=2.1.5", 30 | "java": ">=17" 31 | } 32 | } -------------------------------------------------------------------------------- /ModLoaders/src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "${mod_id}", 4 | "pack_format": 12 5 | } 6 | } -------------------------------------------------------------------------------- /PluginVersion/build.gradle: -------------------------------------------------------------------------------- 1 | import com.hypherionmc.modpublisher.properties.ReleaseType 2 | archivesBaseName = "${mod_name.replace(" ", "")}-Paper" 3 | 4 | dependencies { 5 | // Do not edit or remove 6 | implementation project(":Common") 7 | } 8 | 9 | shadowJar { 10 | dependencies { 11 | relocate 'shadow.kyori', 'net.kyori' 12 | } 13 | } 14 | 15 | publisher { 16 | apiKeys { 17 | nightbloom(System.getenv("PLATFORM_KEY")) 18 | } 19 | 20 | setNightbloomID("sdlink") 21 | setProjectVersion(project.version.toString()) 22 | setReleaseType(ReleaseType.ALPHA) 23 | setChangelog(project.rootProject.file("changelog.md")) 24 | setDisplayName("[Paper 1.19.4 - 1.21.10] Simple Discord Link Paper - ${project.version}") 25 | setGameVersions("1.19.4", "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4", "1.21", "1.21.1", "1.21.3", "1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10") 26 | setLoaders("paper") 27 | setArtifact(shadowJar) 28 | 29 | nightbloomDepends { 30 | required("craterlib") 31 | } 32 | } 33 | 34 | publishing { 35 | publications { 36 | mavenJava(MavenPublication) { 37 | group "com.hypherionmc.sdlink" 38 | artifactId project.archivesBaseName 39 | 40 | artifact(shadowJar) { 41 | builtBy shadowJar 42 | } 43 | artifact sourcesJar 44 | artifact javadocJar 45 | } 46 | } 47 | 48 | repositories { 49 | maven orion.getPublishingMaven() 50 | } 51 | } -------------------------------------------------------------------------------- /PluginVersion/src/main/java/com/hypherionmc/sdlink/loaders/paper/SDLinkPaper.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.loaders.paper; 2 | 3 | import com.hypherionmc.craterlib.api.events.server.CraterServerLifecycleEvent; 4 | import com.hypherionmc.craterlib.core.platform.CommonPlatform; 5 | import com.hypherionmc.craterlib.nojang.server.BridgedMinecraftServer; 6 | import com.hypherionmc.sdlink.server.ServerEvents; 7 | import org.bukkit.plugin.java.JavaPlugin; 8 | 9 | public class SDLinkPaper extends JavaPlugin { 10 | 11 | @Override 12 | public void onLoad() { 13 | 14 | } 15 | 16 | @Override 17 | public void onEnable() { 18 | 19 | } 20 | 21 | @Override 22 | public void onDisable() { 23 | // SDLink unloads before CraterLib does, so we can't receive these events. 24 | // Not that they are needed, but they are "faked" to keep feature parity with the modded versions 25 | BridgedMinecraftServer server = CommonPlatform.INSTANCE.getMCServer(); 26 | ServerEvents.getInstance().onServerStopping(new CraterServerLifecycleEvent.Stopping(server)); 27 | ServerEvents.getInstance().onServerStoppedEvent(new CraterServerLifecycleEvent.Stopped(server)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PluginVersion/src/main/java/com/hypherionmc/sdlink/loaders/paper/SDLinkPaperBootstrap.java: -------------------------------------------------------------------------------- 1 | package com.hypherionmc.sdlink.loaders.paper; 2 | 3 | import com.hypherionmc.craterlib.core.event.CraterEventBus; 4 | import com.hypherionmc.sdlink.server.ServerEvents; 5 | import io.papermc.paper.plugin.bootstrap.BootstrapContext; 6 | import io.papermc.paper.plugin.bootstrap.PluginBootstrap; 7 | 8 | public class SDLinkPaperBootstrap implements PluginBootstrap { 9 | 10 | @Override 11 | public void bootstrap(BootstrapContext bootstrapContext) { 12 | ServerEvents events = ServerEvents.getInstance(); 13 | CraterEventBus.INSTANCE.registerEventListener(events); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /PluginVersion/src/main/resources/paper-plugin.yml: -------------------------------------------------------------------------------- 1 | name: SimpleDiscordLink 2 | version: ${version} 3 | main: com.hypherionmc.sdlink.loaders.paper.SDLinkPaper 4 | description: A simple Minecraft to Discord Chat bot with Whitelisting 5 | author: ${mod_author} 6 | website: https://github.com/firstdarkdev/sdlink 7 | load: STARTUP 8 | bootstrapper: com.hypherionmc.sdlink.loaders.paper.SDLinkPaperBootstrap 9 | api-version: '1.19' 10 | 11 | dependencies: 12 | bootstrap: 13 | CraterLib: 14 | load: BEFORE 15 | required: true 16 | join-classpath: true 17 | server: 18 | CraterLib: 19 | load: BEFORE 20 | required: true 21 | join-classpath: true -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | - REQUIRES [CraterLib](https://www.curseforge.com/minecraft/mc-mods/craterlib) - [Modrinth](https://modrinth.com/mod/craterlib) 2 | - [Online Config Editor](https://editor.firstdark.dev) 3 | - [Documentation](https://sdlink.fdd-docs.com) 4 | - This single jar works on 1.18.2-1.21.9 5 | 6 | *Requires CraterLib 2.1.6 or newer* 7 | 8 | **New Features**: 9 | 10 | - Added support for PlayerRoles Syncing (https://modrinth.com/mod/player-roles) 11 | - Added support for CobblemonGuilds -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #Project 2 | version_major=3 3 | version_minor=3 4 | version_patch=4 5 | version_build=0 6 | 7 | #Mod 8 | mod_author=HypherionSA 9 | mod_id=sdlink 10 | mod_name=Simple Discord Link 11 | 12 | # Shared 13 | minecraft_version=1.20.4 14 | project_group=com.hypherionmc.sdlink 15 | shade_group=com.hypherionmc.sdlink.shaded. 16 | 17 | # Fabric 18 | fabric_loader=0.15.0 19 | 20 | # Dependencies 21 | craterlib=2.1.6+release.0 22 | jda=5.2.2 23 | chewtils=2.0-userPermissions-SNAPSHOT 24 | webhooks=0.8.4 25 | commons4=4.4 26 | oshi=5.8.5 27 | jasypt=1.9.3 28 | maintenance_mode=PIPps60d 29 | 30 | # Publishing 31 | curse_id=541320 32 | modrinth_id=Sh0YauEf 33 | release_type=release 34 | 35 | # Gradle 36 | org.gradle.jvmargs=-Xmx3G 37 | org.gradle.daemon=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypherionmc/sdlink/ae429d457ad1f89745f9be9d393188b4535782c4/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Simple Discord Link Bot 2 | 3 | A simple Minecraft to Discord and vice-versa linker. 4 | 5 | Relies on [craterlib](https://github.com/firstdarkdev/craterlib/) 6 | 7 | --- 8 | 9 | ## Unreleased Builds 10 | 11 | Unreleased builds from commits made to this repo can be downloaded from here: https://nightbloom.cc/project/sdlink -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | maven { 5 | url "https://mcentral.firstdark.dev/releases" 6 | } 7 | maven { 8 | url "https://maven.firstdark.dev/releases" 9 | } 10 | maven { 11 | url "https://maven.firstdark.dev/snapshots" 12 | } 13 | } 14 | } 15 | 16 | rootProject.name = 'sdlink' 17 | include('Common', 'ModLoaders', 'PluginVersion') 18 | 19 | --------------------------------------------------------------------------------