├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── 1.10.2 ├── build.gradle ├── gradle.properties └── src │ └── main │ ├── java │ └── net │ │ └── shadowfacts │ │ └── discordchat │ │ └── one_ten_two │ │ ├── CommandDC.java │ │ ├── DummySender.java │ │ ├── DummyTeleporter.java │ │ ├── ForgeEventHandler.java │ │ ├── OneTenTwoAdapter.java │ │ └── OneTenTwoMod.java │ └── resources │ └── assets │ └── discordchat │ └── default.conf ├── 1.11.2 ├── build.gradle ├── gradle.properties └── src │ └── main │ ├── java │ └── net │ │ └── shadowfacts │ │ └── discordchat │ │ └── one_eleven_two │ │ ├── CommandDC.java │ │ ├── DummySender.java │ │ ├── DummyTeleporter.java │ │ ├── ForgeEventHandler.java │ │ ├── OneElevenTwoAdapter.java │ │ └── OneElevenTwoMod.java │ └── resources │ └── assets │ └── discordchat │ └── default.conf ├── 1.12.2 ├── build.gradle ├── gradle.properties └── src │ └── main │ ├── java │ └── net │ │ └── shadowfacts │ │ └── discordchat │ │ └── one_twelve_two │ │ ├── CommandDC.java │ │ ├── DummySender.java │ │ ├── DummyTeleporter.java │ │ ├── ForgeEventHandler.java │ │ ├── OneTwelveTwoAdapter.java │ │ └── OneTwelveTwoMod.java │ └── resources │ └── assets │ └── discordchat │ └── default.conf ├── 1.7.10 ├── build.gradle ├── gradle.properties └── src │ └── main │ ├── java │ └── net │ │ └── shadowfacts │ │ └── discordchat │ │ └── one_seven_ten │ │ ├── CommandDC.java │ │ ├── DummySender.java │ │ ├── DummyTeleporter.java │ │ ├── FMLEventHandler.java │ │ ├── ForgeEventHandler.java │ │ ├── OneSevenTenAdapter.java │ │ └── OneSevenTenMod.java │ └── resources │ └── assets │ └── discordchat │ └── default.conf ├── 1.8.9 ├── build.gradle ├── gradle.properties └── src │ └── main │ ├── java │ └── net │ │ └── shadowfacts │ │ └── discordchat │ │ └── one_eight_nine │ │ ├── CommandDC.java │ │ ├── DummySender.java │ │ ├── DummyTeleporter.java │ │ ├── ForgeEventHandler.java │ │ ├── OneEightNineAdapter.java │ │ └── OneEightNineMod.java │ └── resources │ └── assets │ └── discordchat │ └── default.conf ├── LICENSE ├── README.md ├── build.gradle ├── core ├── build.gradle ├── gradle.properties ├── libs │ └── JDA-3.3.0_260-withDependencies.jar └── src │ └── main │ ├── java │ └── net │ │ └── shadowfacts │ │ └── discordchat │ │ ├── api │ │ ├── IConfig.java │ │ ├── IDiscordChat.java │ │ ├── ILogger.java │ │ ├── IMessageFormatter.java │ │ ├── IMinecraftAdapter.java │ │ ├── command │ │ │ ├── ICommand.java │ │ │ ├── ICommandManager.java │ │ │ └── exception │ │ │ │ └── CommandException.java │ │ └── permission │ │ │ ├── IPermissionManager.java │ │ │ └── Permission.java │ │ └── core │ │ ├── Config.java │ │ ├── DiscordChat.java │ │ ├── Listener.java │ │ ├── Logger.java │ │ ├── MessageFormatter.java │ │ ├── command │ │ ├── CommandManager.java │ │ ├── exception │ │ │ ├── InvalidPermissionException.java │ │ │ └── InvalidUsageException.java │ │ └── impl │ │ │ ├── meta │ │ │ ├── CommandCommands.java │ │ │ ├── CommandHelp.java │ │ │ ├── CommandReload.java │ │ │ └── CommandRoleID.java │ │ │ ├── minecraft │ │ │ ├── CommandExecute.java │ │ │ ├── CommandOnline.java │ │ │ ├── CommandTPS.java │ │ │ ├── CommandTell.java │ │ │ ├── CommandTime.java │ │ │ └── CommandUnstick.java │ │ │ └── permissions │ │ │ ├── CommandPermission.java │ │ │ └── CommandSetPermission.java │ │ ├── permission │ │ └── PermissionManager.java │ │ └── util │ │ ├── NearestMCColor.java │ │ └── QueuedMessage.java │ └── resources │ └── assets │ └── discordchat │ └── default.conf ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Brief explanation of issue: 2 | 3 | Steps to reproduce: 4 | 5 | - Step 1 6 | - Step 2 7 | 8 | Link to crash report/log in a [gist](https://gist.github.com): 9 | 10 | DiscordChat version: 11 | 12 | Minecraft version: 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | /download 4 | /eclipseBin 5 | 6 | # gradle 7 | **/.gradle/ 8 | **/build 9 | !**/build.gradle 10 | 11 | # ForgeGradle 12 | **/run/ 13 | 14 | # eclipse 15 | /eclipse 16 | /.settings 17 | /.metdata 18 | /.classpath 19 | /.project 20 | /bin 21 | 22 | # intellij 23 | **/out/ 24 | /classes 25 | /.idea 26 | /*.iml 27 | /*.ipr 28 | /*.iws 29 | -------------------------------------------------------------------------------- /1.10.2/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | maven { 5 | name "forge" 6 | url "http://files.minecraftforge.net/maven/" 7 | } 8 | } 9 | dependencies { 10 | classpath "net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT" 11 | } 12 | } 13 | 14 | plugins { 15 | id "com.github.johnrengelman.shadow" version "1.2.3" 16 | } 17 | 18 | apply plugin: "net.minecraftforge.gradle.forge" 19 | 20 | sourceCompatibility = JavaVersion.VERSION_1_8 21 | targetCompatibility = JavaVersion.VERSION_1_8 22 | 23 | version = mc_version + "-" + mod_version 24 | 25 | archivesBaseName = "DiscordChat" 26 | 27 | minecraft { 28 | version = mc_version + "-" + forge_version 29 | runDir = "run" 30 | 31 | mappings = mcp_mappings 32 | 33 | replaceIn "OneTenTwoMod.java" 34 | replace "@VERSION@", mod_version 35 | } 36 | 37 | processResources { 38 | inputs.property "version", project.version 39 | inputs.property "mcversion", project.minecraft.version 40 | 41 | from (sourceSets.main.resources.srcDirs) { 42 | include "mcmod.info" 43 | expand "version": project.version, "mcversion": project.minecraft.version 44 | } 45 | from (sourceSets.main.resources.srcDirs) { 46 | exclude "mcmod.info" 47 | } 48 | } 49 | 50 | repositories { 51 | maven { 52 | name "shadowfacts" 53 | url "http://maven.shadowfacts.net/" 54 | } 55 | flatDir { 56 | dirs "../core/libs" 57 | } 58 | } 59 | 60 | dependencies { 61 | compile project(":core") 62 | } 63 | 64 | shadowJar { 65 | classifier = "" 66 | relocate "com.iwebpp.crypto", "net.shadowfacts.discordchat.repack.com.iwebpp.crypto" 67 | relocate "com.neovisionaries.ws.client", "net.shadowfacts.discordchat.repack.com.neovisionaries.ws.client" 68 | relocate "com.sun.jna", "net.shadowfacts.discordchat.repack.com.sun.jna" 69 | relocate "com.vdurmont.emoji", "net.shadowfacts.discordchat.repack.com.vdurmont.emoji" 70 | relocate "gnu.trove", "net.shadowfacts.discordchat.gnu.trove" 71 | relocate "net.dv8tion.jda", "net.shadowfacts.discordchat.repack.net.dv8tion.jda" 72 | relocate "okhttp3", "net.shadowfacts.discordchat.repack.okhttp3" 73 | relocate "okio", "net.shadowfacts.discordchat.repack.okio" 74 | relocate "org.apache.commons.collections4", "net.shadowfacts.discordchat.repack.org.apache.commons.collections4" 75 | relocate "org.apache.commons.lang3", "net.shadowfacts.discordchat.repack.org.apache.commons.lang3" 76 | relocate "org.json", "net.shadowfacts.discordchat.repack.org.json" 77 | relocate "tomp2p.opuswrapper", "net.shadowfacts.discordchat.repack.tomp2p.opuswrapper" 78 | 79 | dependencies { 80 | include(project(":core")) 81 | include(dependency("JDA:JDA:3.3.0_260:withDependencies")) 82 | include(dependency("com.vdurmont:emoji-java:3.1.3")) 83 | include(dependency("net.shadowfacts:ShadowLib:1.9.0")) 84 | } 85 | } 86 | 87 | reobf { shadowJar { mappingType = "SEARGE" } } 88 | tasks.reobfShadowJar.mustRunAfter shadowJar 89 | -------------------------------------------------------------------------------- /1.10.2/gradle.properties: -------------------------------------------------------------------------------- 1 | group = net.shadowfacts 2 | mc_version = 1.10.2 3 | mcp_mappings = stable_29 4 | forge_version = 12.18.2.2151 -------------------------------------------------------------------------------- /1.10.2/src/main/java/net/shadowfacts/discordchat/one_ten_two/CommandDC.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_ten_two; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.dv8tion.jda.core.entities.Role; 5 | import net.minecraft.command.CommandBase; 6 | import net.minecraft.command.CommandException; 7 | import net.minecraft.command.ICommandSender; 8 | import net.minecraft.command.WrongUsageException; 9 | import net.minecraft.server.MinecraftServer; 10 | import net.minecraft.util.text.TextComponentString; 11 | import net.minecraft.util.text.TextComponentTranslation; 12 | import net.shadowfacts.discordchat.api.IConfig; 13 | import net.shadowfacts.discordchat.api.IDiscordChat; 14 | import net.shadowfacts.discordchat.api.ILogger; 15 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 16 | import net.shadowfacts.discordchat.api.permission.Permission; 17 | 18 | import java.io.IOException; 19 | import java.util.Arrays; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.function.BiConsumer; 24 | 25 | /** 26 | * @author shadowfacts 27 | */ 28 | public class CommandDC extends CommandBase { 29 | 30 | private IDiscordChat discordChat; 31 | private ILogger logger; 32 | private IConfig config; 33 | private IPermissionManager permissionManager; 34 | 35 | private Map> subcommands = new HashMap<>(); 36 | 37 | public CommandDC(IDiscordChat discordChat) { 38 | this.discordChat = discordChat; 39 | logger = discordChat.getLogger(); 40 | config = discordChat.getConfig(); 41 | permissionManager = discordChat.getPermissionManager(); 42 | 43 | subcommands.put("setpermission", this::setPermission); 44 | subcommands.put("tell", this::tell); 45 | } 46 | 47 | @Override 48 | public String getName() { 49 | return "discordchat"; 50 | } 51 | 52 | @Override 53 | public String getUsage(ICommandSender sender) { 54 | return "/discordchat "; 55 | } 56 | 57 | @Override 58 | public List getAliases() { 59 | return ImmutableList.of("dc"); 60 | } 61 | 62 | @Override 63 | public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { 64 | if (args.length < 1) { 65 | throw new WrongUsageException(getUsage(sender)); 66 | } 67 | 68 | String command = args[0].toLowerCase(); 69 | args = Arrays.copyOfRange(args, 1, args.length); 70 | 71 | if (!subcommands.containsKey(command)) { 72 | throw new WrongUsageException(getUsage(sender)); 73 | } 74 | 75 | subcommands.get(command).accept(sender, args); 76 | } 77 | 78 | private void setPermission(ICommandSender sender, String[] args) { 79 | if (args.length < 2) { 80 | wrongUsage(sender, "/discordchat setPermission "); 81 | } 82 | 83 | Permission permission = Permission.valueOf(args[args.length - 1].toUpperCase()); 84 | 85 | String role = String.join(" ", Arrays.copyOfRange(args, 0, args.length - 1)); 86 | List roles = discordChat.getJDA().getGuildById(config.getServerID()).getRolesByName(role, true); 87 | if (roles.isEmpty()) { 88 | sender.sendMessage(new TextComponentString("No such role: " + role)); 89 | return; 90 | } 91 | 92 | permissionManager.set(roles.get(0), permission); 93 | 94 | try { 95 | permissionManager.save(); 96 | sender.sendMessage(new TextComponentString("Permissions updated")); 97 | } catch (IOException e) { 98 | logger.error(e, "Unable to save permissions"); 99 | } 100 | } 101 | 102 | private void tell(ICommandSender sender, String[] args) { 103 | if (args.length < 2) { 104 | wrongUsage(sender, "/discordchat tell "); 105 | } else { 106 | String user = args[0]; 107 | String message = ""; 108 | for (int i = 1; i < args.length; i++) { 109 | message += args[i]; 110 | if (i != args.length - 1) message += " "; 111 | } 112 | discordChat.sendPrivateMessage(sender.getName(), message, user); 113 | } 114 | } 115 | 116 | private void wrongUsage(ICommandSender sender, String usage) { 117 | sender.sendMessage(new TextComponentTranslation("commands.generic.usage", usage)); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /1.10.2/src/main/java/net/shadowfacts/discordchat/one_ten_two/DummySender.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_ten_two; 2 | 3 | import net.minecraft.command.CommandResultStats; 4 | import net.minecraft.command.ICommandSender; 5 | import net.minecraft.entity.Entity; 6 | import net.minecraft.server.MinecraftServer; 7 | import net.minecraft.util.math.BlockPos; 8 | import net.minecraft.util.math.Vec3d; 9 | import net.minecraft.util.text.ITextComponent; 10 | import net.minecraft.util.text.TextComponentString; 11 | import net.minecraft.world.World; 12 | import net.minecraftforge.fml.common.FMLCommonHandler; 13 | 14 | import javax.annotation.Nullable; 15 | 16 | /** 17 | * @author shadowfacts 18 | */ 19 | public class DummySender implements ICommandSender { 20 | 21 | public static final ICommandSender INSTANCE = new DummySender(); 22 | 23 | @Override 24 | public String getName() { 25 | return "[DiscordChat]"; 26 | } 27 | 28 | @Override 29 | public ITextComponent getDisplayName() { 30 | return new TextComponentString(getName()); 31 | } 32 | 33 | @Override 34 | public void sendMessage(ITextComponent component) { 35 | OneTenTwoMod.discordChat.sendMessage(OneTenTwoMod.discordChat.getFormatter().command("```" + component.getUnformattedText() + "```")); 36 | } 37 | 38 | @Override 39 | public boolean canUseCommand(int permLevel, String commandName) { 40 | return true; 41 | } 42 | 43 | @Override 44 | public BlockPos getPosition() { 45 | return BlockPos.ORIGIN; 46 | } 47 | 48 | @Override 49 | public Vec3d getPositionVector() { 50 | return Vec3d.ZERO; 51 | } 52 | 53 | @Override 54 | public World getEntityWorld() { 55 | return getServer().worldServerForDimension(0); 56 | } 57 | 58 | @Nullable 59 | @Override 60 | public Entity getCommandSenderEntity() { 61 | return null; 62 | } 63 | 64 | @Override 65 | public boolean sendCommandFeedback() { 66 | return false; 67 | } 68 | 69 | @Override 70 | public void setCommandStat(CommandResultStats.Type type, int amount) { 71 | 72 | } 73 | 74 | @Nullable 75 | @Override 76 | public MinecraftServer getServer() { 77 | return FMLCommonHandler.instance().getMinecraftServerInstance(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /1.10.2/src/main/java/net/shadowfacts/discordchat/one_ten_two/DummyTeleporter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_ten_two; 2 | 3 | import net.minecraft.entity.Entity; 4 | import net.minecraft.world.Teleporter; 5 | import net.minecraft.world.WorldServer; 6 | 7 | /** 8 | * @author shadowfacts 9 | */ 10 | public class DummyTeleporter extends Teleporter { 11 | 12 | public DummyTeleporter(WorldServer world) { 13 | super(world); 14 | } 15 | 16 | @Override 17 | public void placeInPortal(Entity entity, float rotationYaw) { 18 | 19 | } 20 | 21 | @Override 22 | public boolean placeInExistingPortal(Entity entity, float rotationYaw) { 23 | entity.motionX = 0; 24 | entity.motionY = 0; 25 | entity.motionZ = 0; 26 | entity.fallDistance = 0; 27 | return true; 28 | } 29 | 30 | @Override 31 | public boolean makePortal(Entity entity) { 32 | return true; 33 | } 34 | 35 | @Override 36 | public void removeStalePortalLocations(long worldTime) { 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /1.10.2/src/main/java/net/shadowfacts/discordchat/one_ten_two/ForgeEventHandler.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_ten_two; 2 | 3 | import net.minecraft.entity.player.EntityPlayer; 4 | import net.minecraft.entity.player.EntityPlayerMP; 5 | import net.minecraft.util.text.ITextComponent; 6 | import net.minecraft.util.text.TextComponentString; 7 | import net.minecraft.util.text.TextComponentTranslation; 8 | import net.minecraftforge.common.util.FakePlayer; 9 | import net.minecraftforge.event.ServerChatEvent; 10 | import net.minecraftforge.event.entity.living.LivingDeathEvent; 11 | import net.minecraftforge.event.entity.player.AchievementEvent; 12 | import net.minecraftforge.fml.common.FMLCommonHandler; 13 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 14 | import net.minecraftforge.fml.common.gameevent.PlayerEvent; 15 | 16 | /** 17 | * @author shadowfacts 18 | */ 19 | public class ForgeEventHandler { 20 | 21 | @SubscribeEvent 22 | public void onServerChat(ServerChatEvent event) { 23 | String message = OneTenTwoMod.discordChat.filterMCMessage(event.getMessage()); 24 | if (message != null) { 25 | OneTenTwoMod.discordChat.sendMessage(OneTenTwoMod.discordChat.getFormatter().fromMC(event.getPlayer().getName(), message)); 26 | } 27 | } 28 | 29 | @SubscribeEvent 30 | public void onLivingDeath(LivingDeathEvent event) { 31 | if (OneTenTwoMod.config.sendDeathMessages() && event.getEntityLiving() instanceof EntityPlayer && !(event.getEntityLiving() instanceof FakePlayer) && !event.getEntity().world.isRemote) { 32 | EntityPlayer player = (EntityPlayer)event.getEntityLiving(); 33 | OneTenTwoMod.discordChat.sendMessage(OneTenTwoMod.discordChat.getFormatter().death(player.getName(), player.getCombatTracker().getDeathMessage().getUnformattedText())); 34 | } 35 | } 36 | 37 | @SubscribeEvent 38 | public void onPlayerReceiveAchievement(AchievementEvent event) { 39 | if (OneTenTwoMod.config.sendAchievementMessages() && event.getEntityPlayer() instanceof EntityPlayerMP) { 40 | if (((EntityPlayerMP) event.getEntityPlayer()).getStatFile().hasAchievementUnlocked(event.getAchievement())) { 41 | return; 42 | } 43 | if (!((EntityPlayerMP) event.getEntityPlayer()).getStatFile().canUnlockAchievement(event.getAchievement())) { 44 | return; 45 | } 46 | ITextComponent achievementComponent = event.getAchievement().getStatName(); 47 | ITextComponent achievementText = new TextComponentString("[").appendSibling(achievementComponent).appendText("]"); 48 | OneTenTwoMod.discordChat.sendMessage(OneTenTwoMod.discordChat.getFormatter().achievement(event.getEntityPlayer().getName(), achievementText.getUnformattedText())); 49 | } 50 | } 51 | 52 | @SubscribeEvent 53 | public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { 54 | if (OneTenTwoMod.config.sendJoinLeaveMessages()) { 55 | OneTenTwoMod.discordChat.sendMessage(OneTenTwoMod.discordChat.getFormatter().join(event.player.getName())); 56 | } 57 | } 58 | 59 | @SubscribeEvent 60 | public void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { 61 | if (OneTenTwoMod.config.sendJoinLeaveMessages()) { 62 | OneTenTwoMod.discordChat.sendMessage(OneTenTwoMod.discordChat.getFormatter().leave(event.player.getName())); 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /1.10.2/src/main/java/net/shadowfacts/discordchat/one_ten_two/OneTenTwoAdapter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_ten_two; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import net.minecraft.entity.player.EntityPlayer; 5 | import net.minecraft.entity.player.EntityPlayerMP; 6 | import net.minecraft.nbt.NBTTagCompound; 7 | import net.minecraft.server.MinecraftServer; 8 | import net.minecraft.server.management.PlayerList; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.util.text.TextComponentString; 11 | import net.minecraft.world.WorldServer; 12 | import net.minecraft.world.storage.IPlayerFileData; 13 | import net.minecraftforge.common.DimensionManager; 14 | import net.minecraftforge.common.ForgeHooks; 15 | import net.minecraftforge.common.util.FakePlayer; 16 | import net.minecraftforge.common.util.FakePlayerFactory; 17 | import net.minecraftforge.fml.common.FMLCommonHandler; 18 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 19 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 20 | 21 | import java.util.Set; 22 | import java.util.stream.Collectors; 23 | 24 | /** 25 | * @author shadowfacts 26 | */ 27 | public class OneTenTwoAdapter implements IMinecraftAdapter { 28 | 29 | @Override 30 | public void sendMessage(String message) { 31 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 32 | if (server != null) { 33 | server.getPlayerList().sendChatMsg(ForgeHooks.newChatWithLinks(message)); 34 | } 35 | } 36 | 37 | @Override 38 | public void sendMessageToPlayer(String message, String player) { 39 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 40 | EntityPlayer mcPlayer = server.getPlayerList().getPlayerByUsername(player); 41 | mcPlayer.sendMessage(ForgeHooks.newChatWithLinks(message)); 42 | } 43 | 44 | @Override 45 | public int[] getAllDimensions() { 46 | Integer[] boxed = DimensionManager.getIDs(); 47 | int[] unboxed = new int[boxed.length]; 48 | for (int i = 0; i < boxed.length; i++) { 49 | unboxed[i] = boxed[i]; 50 | } 51 | return unboxed; 52 | } 53 | 54 | @Override 55 | public double getTickTime(int dimension) { 56 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 57 | long[] tickTimes = server.worldTickTimes.get(dimension); 58 | long sum = 0; 59 | for (int i = 0; i < tickTimes.length; i++) { 60 | sum += tickTimes[i]; 61 | } 62 | return sum / tickTimes.length * 1.0E-6D; 63 | } 64 | 65 | @Override 66 | public Set getOnlinePlayers() { 67 | return FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().getPlayers().stream() 68 | .map(EntityPlayer::getName) 69 | .collect(Collectors.toSet()); 70 | } 71 | 72 | @Override 73 | public void executeCommand(String command) { 74 | FMLCommonHandler.instance().getMinecraftServerInstance().callFromMainThread(() -> { 75 | FMLCommonHandler.instance().getMinecraftServerInstance().getCommandManager().executeCommand(DummySender.INSTANCE, command); 76 | return null; 77 | }); 78 | } 79 | 80 | @Override 81 | public String teleportPlayerToSpawn(String username) { 82 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 83 | WorldServer world = server.worldServerForDimension(0); 84 | BlockPos spawn = world.getSpawnPoint(); 85 | PlayerList playerList = server.getPlayerList(); 86 | EntityPlayerMP player = playerList.getPlayerByUsername(username); 87 | if (player != null) { 88 | if (player.dimension != 0) { 89 | int original = player.dimension; 90 | playerList.transferPlayerToDimension(player, 0, new DummyTeleporter(world)); 91 | 92 | if (original == 1 && player.isEntityAlive()) { 93 | world.spawnEntity(player); 94 | world.updateEntityWithOptionalForce(player, false); 95 | } 96 | } 97 | player.setPositionAndUpdate(spawn.getX() + 0.5d, spawn.getY(), spawn.getZ() + 0.5d); 98 | } else { 99 | GameProfile profile = server.getPlayerProfileCache().getGameProfileForUsername(username); 100 | 101 | if (profile != null && profile.isComplete()) { 102 | FakePlayer fakePlayer = FakePlayerFactory.get(world, profile); 103 | IPlayerFileData saveHandler = world.getSaveHandler().getPlayerNBTManager(); 104 | NBTTagCompound tag = saveHandler.readPlayerData(fakePlayer); 105 | 106 | if (tag == null) { 107 | return "Unknown player: " + username; 108 | } 109 | 110 | fakePlayer.dimension = 0; 111 | fakePlayer.posX = spawn.getX() + 0.5d; 112 | fakePlayer.posY = spawn.getY(); 113 | fakePlayer.posZ = spawn.getZ() + 0.5d; 114 | 115 | saveHandler.writePlayerData(fakePlayer); 116 | } else { 117 | return "Unknown player: " + username; 118 | } 119 | } 120 | return null; 121 | } 122 | 123 | @Override 124 | public long getWorldTime(int dimension) { 125 | return FMLCommonHandler.instance().getMinecraftServerInstance().worldServerForDimension(dimension).getWorldTime(); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /1.10.2/src/main/java/net/shadowfacts/discordchat/one_ten_two/OneTenTwoMod.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_ten_two; 2 | 3 | import net.minecraftforge.common.MinecraftForge; 4 | import net.minecraftforge.fml.common.Mod; 5 | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; 6 | import net.minecraftforge.fml.common.event.FMLServerStartedEvent; 7 | import net.minecraftforge.fml.common.event.FMLServerStartingEvent; 8 | import net.minecraftforge.fml.common.event.FMLServerStoppedEvent; 9 | import net.shadowfacts.discordchat.api.IConfig; 10 | import net.shadowfacts.discordchat.api.IDiscordChat; 11 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 12 | import net.shadowfacts.discordchat.core.DiscordChat; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | 17 | /** 18 | * @author shadowfacts 19 | */ 20 | @Mod(modid = OneTenTwoMod.MOD_ID, name = OneTenTwoMod.NAME, version = OneTenTwoMod.VERSION, acceptableRemoteVersions = "*") 21 | public class OneTenTwoMod { 22 | 23 | public static final String MOD_ID = "discordchat"; 24 | public static final String NAME = "Discord Chat"; 25 | public static final String VERSION = "@VERSION@"; 26 | 27 | public static IMinecraftAdapter minecraftAdapter = new OneTenTwoAdapter(); 28 | public static IConfig config; 29 | public static IDiscordChat discordChat; 30 | 31 | @Mod.EventHandler 32 | public void preInit(FMLPreInitializationEvent event) throws IOException { 33 | discordChat = new DiscordChat(new File(event.getModConfigurationDirectory(), "shadowfacts/DiscordChat/"), minecraftAdapter); 34 | config = discordChat.getConfig(); 35 | discordChat.connect(); 36 | 37 | MinecraftForge.EVENT_BUS.register(new ForgeEventHandler()); 38 | } 39 | 40 | @Mod.EventHandler 41 | public void serverStarting(FMLServerStartingEvent event) { 42 | event.registerServerCommand(new CommandDC(discordChat)); 43 | } 44 | 45 | @Mod.EventHandler 46 | public void serverStarted(FMLServerStartedEvent event) { 47 | discordChat.start(); 48 | if (config.sendServerOnlineOfflineMessages()) discordChat.sendMessage("Server is online"); 49 | } 50 | 51 | @Mod.EventHandler 52 | public void serverStopped(FMLServerStoppedEvent event) { 53 | if (config.sendServerOnlineOfflineMessages()) discordChat.sendMessage("Server is offline"); 54 | discordChat.stop(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /1.10.2/src/main/resources/assets/discordchat/default.conf: -------------------------------------------------------------------------------- 1 | discordchat { 2 | discord { 3 | token = "" 4 | server = "" 5 | channels = [] 6 | } 7 | 8 | commands { 9 | prefix = "!" 10 | 11 | permission { 12 | commands = "global" 13 | help = "global" 14 | reload = "admin" 15 | roleid = "global" 16 | exec = "admin" 17 | online = "global" 18 | tps = "global" 19 | unstick = "approved" 20 | time = "global" 21 | permission = "global" 22 | setPermission = "admin" 23 | tell = "global" 24 | } 25 | } 26 | 27 | relay { 28 | deaths = true 29 | achievements = true 30 | joinleave = true 31 | onlineoffline = true 32 | } 33 | 34 | filter { 35 | mode = "none" 36 | filter = "" 37 | stripFilter = false 38 | } 39 | 40 | format { 41 | # Default color if the role of the user has no color on Discord (eg. @everyone) 42 | defaultColor = "§f" 43 | 44 | # Format for a normal message from MC to Discord. 45 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 46 | fromMC = "MC » <$1> $2" 47 | 48 | # Format for a normal message from Discord to MC. 49 | # $1 will be replaced with the channel, $2 will be replaced with the sender's username, $3 will be replaced with the message, and $4 will be replaced with the MC color (e.g. §3) closest to the Discord user's color. 50 | fromDiscord = "#$1 » <$4$2§f> $3" 51 | 52 | # Format for a private message from MC to Discord. 53 | # $1 will be replaced with the sender's username and $2 will be replaced with the message. 54 | fromMCPrivate = "MC » <$1> $2" 55 | 56 | # Format for a private message from Discord to MC. 57 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 58 | fromDiscordPrivate = "Discord » <$1> $2" 59 | 60 | # Format for a player death message from MC to Discord. 61 | # $1 will be replaced with the player's username and $2 will be replaced with the death message 62 | death = "MC » $2" 63 | 64 | # Format for a player achievement message from MC to Discord. 65 | # $1 will be replaced with the player's username and $2 will be replaced with the achievement 66 | achievement = "MC » $1 has just earned the achievement $2" 67 | 68 | # Format for a player join message from MC to Discord. 69 | # $1 will be replaced with the player's username 70 | join = "MC » $1 joined the game" 71 | 72 | # Format for a player leave message from MC to Discord. 73 | # $1 will be replaced with the player's username 74 | leave = "MC » $1 left the game" 75 | 76 | # Format applied to every command response message. 77 | # Used for differentiating command responses from multiple bot instances. 78 | # $1 will be replaced with the message 79 | command = "$1" 80 | } 81 | } -------------------------------------------------------------------------------- /1.11.2/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | maven { 5 | name "forge" 6 | url "http://files.minecraftforge.net/maven/" 7 | } 8 | } 9 | dependencies { 10 | classpath "net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT" 11 | } 12 | } 13 | 14 | plugins { 15 | id "com.github.johnrengelman.shadow" version "1.2.3" 16 | } 17 | 18 | apply plugin: "net.minecraftforge.gradle.forge" 19 | 20 | sourceCompatibility = JavaVersion.VERSION_1_8 21 | targetCompatibility = JavaVersion.VERSION_1_8 22 | 23 | version = mc_version + "-" + mod_version 24 | 25 | archivesBaseName = "DiscordChat" 26 | 27 | minecraft { 28 | version = mc_version + "-" + forge_version 29 | runDir = "run" 30 | 31 | mappings = mcp_mappings 32 | 33 | replaceIn "OneElevenTwoMod.java" 34 | replace "@VERSION@", mod_version 35 | } 36 | 37 | processResources { 38 | inputs.property "version", project.version 39 | inputs.property "mcversion", project.minecraft.version 40 | 41 | from (sourceSets.main.resources.srcDirs) { 42 | include "mcmod.info" 43 | expand "version": project.version, "mcversion": project.minecraft.version 44 | } 45 | from (sourceSets.main.resources.srcDirs) { 46 | exclude "mcmod.info" 47 | } 48 | } 49 | 50 | repositories { 51 | maven { 52 | name "shadowfacts" 53 | url "http://maven.shadowfacts.net/" 54 | } 55 | flatDir { 56 | dirs "../core/libs" 57 | } 58 | } 59 | 60 | dependencies { 61 | compile project(":core") 62 | } 63 | 64 | shadowJar { 65 | classifier = "" 66 | relocate "com.iwebpp.crypto", "net.shadowfacts.discordchat.repack.com.iwebpp.crypto" 67 | relocate "com.neovisionaries.ws.client", "net.shadowfacts.discordchat.repack.com.neovisionaries.ws.client" 68 | relocate "com.sun.jna", "net.shadowfacts.discordchat.repack.com.sun.jna" 69 | relocate "com.vdurmont.emoji", "net.shadowfacts.discordchat.repack.com.vdurmont.emoji" 70 | relocate "gnu.trove", "net.shadowfacts.discordchat.gnu.trove" 71 | relocate "net.dv8tion.jda", "net.shadowfacts.discordchat.repack.net.dv8tion.jda" 72 | relocate "okhttp3", "net.shadowfacts.discordchat.repack.okhttp3" 73 | relocate "okio", "net.shadowfacts.discordchat.repack.okio" 74 | relocate "org.apache.commons.collections4", "net.shadowfacts.discordchat.repack.org.apache.commons.collections4" 75 | relocate "org.apache.commons.lang3", "net.shadowfacts.discordchat.repack.org.apache.commons.lang3" 76 | relocate "org.json", "net.shadowfacts.discordchat.repack.org.json" 77 | relocate "tomp2p.opuswrapper", "net.shadowfacts.discordchat.repack.tomp2p.opuswrapper" 78 | 79 | dependencies { 80 | include(project(":core")) 81 | include(dependency("JDA:JDA:3.3.0_260:withDependencies")) 82 | include(dependency("com.vdurmont:emoji-java:3.1.3")) 83 | include(dependency("net.shadowfacts:ShadowLib:1.9.0")) 84 | } 85 | } 86 | 87 | reobf { shadowJar { mappingType = "SEARGE" } } 88 | tasks.reobfShadowJar.mustRunAfter shadowJar 89 | -------------------------------------------------------------------------------- /1.11.2/gradle.properties: -------------------------------------------------------------------------------- 1 | group = net.shadowfacts 2 | mc_version = 1.11.2 3 | mcp_mappings = snapshot_20170112 4 | forge_version = 13.20.0.2214 -------------------------------------------------------------------------------- /1.11.2/src/main/java/net/shadowfacts/discordchat/one_eleven_two/CommandDC.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eleven_two; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.dv8tion.jda.core.entities.Role; 5 | import net.minecraft.command.CommandBase; 6 | import net.minecraft.command.CommandException; 7 | import net.minecraft.command.ICommandSender; 8 | import net.minecraft.command.WrongUsageException; 9 | import net.minecraft.server.MinecraftServer; 10 | import net.minecraft.util.text.TextComponentString; 11 | import net.minecraft.util.text.TextComponentTranslation; 12 | import net.shadowfacts.discordchat.api.IConfig; 13 | import net.shadowfacts.discordchat.api.IDiscordChat; 14 | import net.shadowfacts.discordchat.api.ILogger; 15 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 16 | import net.shadowfacts.discordchat.api.permission.Permission; 17 | 18 | import java.io.IOException; 19 | import java.util.Arrays; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.function.BiConsumer; 24 | 25 | /** 26 | * @author shadowfacts 27 | */ 28 | public class CommandDC extends CommandBase { 29 | 30 | private IDiscordChat discordChat; 31 | private ILogger logger; 32 | private IConfig config; 33 | private IPermissionManager permissionManager; 34 | 35 | private Map> subcommands = new HashMap<>(); 36 | 37 | public CommandDC(IDiscordChat discordChat) { 38 | this.discordChat = discordChat; 39 | logger = discordChat.getLogger(); 40 | config = discordChat.getConfig(); 41 | permissionManager = discordChat.getPermissionManager(); 42 | 43 | subcommands.put("setpermission", this::setPermission); 44 | subcommands.put("tell", this::tell); 45 | } 46 | 47 | @Override 48 | public String getName() { 49 | return "discordchat"; 50 | } 51 | 52 | @Override 53 | public String getUsage(ICommandSender sender) { 54 | return "/discordchat "; 55 | } 56 | 57 | @Override 58 | public List getAliases() { 59 | return ImmutableList.of("dc"); 60 | } 61 | 62 | @Override 63 | public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { 64 | if (args.length < 1) { 65 | throw new WrongUsageException(getUsage(sender)); 66 | } 67 | 68 | String command = args[0].toLowerCase(); 69 | args = Arrays.copyOfRange(args, 1, args.length); 70 | 71 | if (!subcommands.containsKey(command)) { 72 | throw new WrongUsageException(getUsage(sender)); 73 | } 74 | 75 | subcommands.get(command).accept(sender, args); 76 | } 77 | 78 | private void setPermission(ICommandSender sender, String[] args) { 79 | if (args.length < 2) { 80 | wrongUsage(sender, "/discordchat setPermission "); 81 | } 82 | 83 | Permission permission = Permission.valueOf(args[args.length - 1].toUpperCase()); 84 | 85 | String role = String.join(" ", Arrays.copyOfRange(args, 0, args.length - 1)); 86 | List roles = discordChat.getJDA().getGuildById(config.getServerID()).getRolesByName(role, true); 87 | if (roles.isEmpty()) { 88 | sender.sendMessage(new TextComponentString("No such role: " + role)); 89 | return; 90 | } 91 | 92 | permissionManager.set(roles.get(0), permission); 93 | 94 | try { 95 | permissionManager.save(); 96 | sender.sendMessage(new TextComponentString("Permissions updated")); 97 | } catch (IOException e) { 98 | logger.error(e, "Unable to save permissions"); 99 | } 100 | } 101 | 102 | private void tell(ICommandSender sender, String[] args) { 103 | if (args.length < 2) { 104 | wrongUsage(sender, "/discordchat tell "); 105 | } else { 106 | String user = args[0]; 107 | String message = ""; 108 | for (int i = 1; i < args.length; i++) { 109 | message += args[i]; 110 | if (i != args.length - 1) message += " "; 111 | } 112 | discordChat.sendPrivateMessage(sender.getName(), message, user); 113 | } 114 | } 115 | 116 | private void wrongUsage(ICommandSender sender, String usage) { 117 | sender.sendMessage(new TextComponentTranslation("commands.generic.usage", usage)); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /1.11.2/src/main/java/net/shadowfacts/discordchat/one_eleven_two/DummySender.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eleven_two; 2 | 3 | import net.minecraft.command.CommandResultStats; 4 | import net.minecraft.command.ICommandSender; 5 | import net.minecraft.entity.Entity; 6 | import net.minecraft.server.MinecraftServer; 7 | import net.minecraft.util.math.BlockPos; 8 | import net.minecraft.util.math.Vec3d; 9 | import net.minecraft.util.text.ITextComponent; 10 | import net.minecraft.util.text.TextComponentString; 11 | import net.minecraft.world.World; 12 | import net.minecraftforge.fml.common.FMLCommonHandler; 13 | 14 | import javax.annotation.Nullable; 15 | 16 | /** 17 | * @author shadowfacts 18 | */ 19 | public class DummySender implements ICommandSender { 20 | 21 | public static final ICommandSender INSTANCE = new DummySender(); 22 | 23 | @Override 24 | public String getName() { 25 | return "[DiscordChat]"; 26 | } 27 | 28 | @Override 29 | public ITextComponent getDisplayName() { 30 | return new TextComponentString(getName()); 31 | } 32 | 33 | @Override 34 | public void sendMessage(ITextComponent component) { 35 | OneElevenTwoMod.discordChat.sendMessage(OneElevenTwoMod.discordChat.getFormatter().command("```" + component.getUnformattedText() + "```")); 36 | } 37 | 38 | @Override 39 | public boolean canUseCommand(int permLevel, String commandName) { 40 | return true; 41 | } 42 | 43 | @Override 44 | public BlockPos getPosition() { 45 | return BlockPos.ORIGIN; 46 | } 47 | 48 | @Override 49 | public Vec3d getPositionVector() { 50 | return Vec3d.ZERO; 51 | } 52 | 53 | @Override 54 | public World getEntityWorld() { 55 | return getServer().worldServerForDimension(0); 56 | } 57 | 58 | @Nullable 59 | @Override 60 | public Entity getCommandSenderEntity() { 61 | return null; 62 | } 63 | 64 | @Override 65 | public boolean sendCommandFeedback() { 66 | return false; 67 | } 68 | 69 | @Override 70 | public void setCommandStat(CommandResultStats.Type type, int amount) { 71 | 72 | } 73 | 74 | @Nullable 75 | @Override 76 | public MinecraftServer getServer() { 77 | return FMLCommonHandler.instance().getMinecraftServerInstance(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /1.11.2/src/main/java/net/shadowfacts/discordchat/one_eleven_two/DummyTeleporter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eleven_two; 2 | 3 | import net.minecraft.entity.Entity; 4 | import net.minecraft.world.Teleporter; 5 | import net.minecraft.world.WorldServer; 6 | 7 | /** 8 | * @author shadowfacts 9 | */ 10 | public class DummyTeleporter extends Teleporter { 11 | 12 | public DummyTeleporter(WorldServer world) { 13 | super(world); 14 | } 15 | 16 | @Override 17 | public void placeInPortal(Entity entity, float rotationYaw) { 18 | 19 | } 20 | 21 | @Override 22 | public boolean placeInExistingPortal(Entity entity, float rotationYaw) { 23 | entity.motionX = 0; 24 | entity.motionY = 0; 25 | entity.motionZ = 0; 26 | entity.fallDistance = 0; 27 | return true; 28 | } 29 | 30 | @Override 31 | public boolean makePortal(Entity entity) { 32 | return true; 33 | } 34 | 35 | @Override 36 | public void removeStalePortalLocations(long worldTime) { 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /1.11.2/src/main/java/net/shadowfacts/discordchat/one_eleven_two/ForgeEventHandler.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eleven_two; 2 | 3 | import net.minecraft.entity.player.EntityPlayer; 4 | import net.minecraft.entity.player.EntityPlayerMP; 5 | import net.minecraft.util.text.ITextComponent; 6 | import net.minecraft.util.text.TextComponentString; 7 | import net.minecraftforge.common.util.FakePlayer; 8 | import net.minecraftforge.event.ServerChatEvent; 9 | import net.minecraftforge.event.entity.living.LivingDeathEvent; 10 | import net.minecraftforge.event.entity.player.AchievementEvent; 11 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 12 | import net.minecraftforge.fml.common.gameevent.PlayerEvent; 13 | 14 | /** 15 | * @author shadowfacts 16 | */ 17 | public class ForgeEventHandler { 18 | 19 | @SubscribeEvent 20 | public void onServerChat(ServerChatEvent event) { 21 | String message = OneElevenTwoMod.discordChat.filterMCMessage(event.getMessage()); 22 | if (message != null) { 23 | OneElevenTwoMod.discordChat.sendMessage(OneElevenTwoMod.discordChat.getFormatter().fromMC(event.getPlayer().getName(), message)); 24 | } 25 | } 26 | 27 | @SubscribeEvent 28 | public void onLivingDeath(LivingDeathEvent event) { 29 | if (OneElevenTwoMod.config.sendDeathMessages() && event.getEntityLiving() instanceof EntityPlayer && !(event.getEntityLiving() instanceof FakePlayer) && !event.getEntity().world.isRemote) { 30 | EntityPlayer player = (EntityPlayer)event.getEntityLiving(); 31 | OneElevenTwoMod.discordChat.sendMessage(OneElevenTwoMod.discordChat.getFormatter().death(player.getName(), player.getCombatTracker().getDeathMessage().getUnformattedText())); 32 | } 33 | } 34 | 35 | @SubscribeEvent 36 | public void onPlayerReceiveAchievement(AchievementEvent event) { 37 | if (OneElevenTwoMod.config.sendAchievementMessages() && event.getEntityPlayer() instanceof EntityPlayerMP) { 38 | if (((EntityPlayerMP) event.getEntityPlayer()).getStatFile().hasAchievementUnlocked(event.getAchievement())) { 39 | return; 40 | } 41 | if (!((EntityPlayerMP) event.getEntityPlayer()).getStatFile().canUnlockAchievement(event.getAchievement())) { 42 | return; 43 | } 44 | ITextComponent achievementComponent = event.getAchievement().getStatName(); 45 | ITextComponent achievementText = new TextComponentString("[").appendSibling(achievementComponent).appendText("]"); 46 | OneElevenTwoMod.discordChat.sendMessage(OneElevenTwoMod.discordChat.getFormatter().achievement(event.getEntityPlayer().getName(), achievementText.getUnformattedText())); 47 | } 48 | } 49 | 50 | @SubscribeEvent 51 | public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { 52 | if (OneElevenTwoMod.config.sendJoinLeaveMessages()) { 53 | OneElevenTwoMod.discordChat.sendMessage(OneElevenTwoMod.discordChat.getFormatter().join(event.player.getName())); 54 | } 55 | } 56 | 57 | @SubscribeEvent 58 | public void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { 59 | if (OneElevenTwoMod.config.sendJoinLeaveMessages()) { 60 | OneElevenTwoMod.discordChat.sendMessage(OneElevenTwoMod.discordChat.getFormatter().leave(event.player.getName())); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /1.11.2/src/main/java/net/shadowfacts/discordchat/one_eleven_two/OneElevenTwoAdapter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eleven_two; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import net.minecraft.entity.player.EntityPlayer; 5 | import net.minecraft.entity.player.EntityPlayerMP; 6 | import net.minecraft.nbt.NBTTagCompound; 7 | import net.minecraft.server.MinecraftServer; 8 | import net.minecraft.server.management.PlayerList; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.world.WorldServer; 11 | import net.minecraft.world.storage.IPlayerFileData; 12 | import net.minecraftforge.common.DimensionManager; 13 | import net.minecraftforge.common.ForgeHooks; 14 | import net.minecraftforge.common.util.FakePlayer; 15 | import net.minecraftforge.common.util.FakePlayerFactory; 16 | import net.minecraftforge.fml.common.FMLCommonHandler; 17 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 18 | 19 | import java.util.Set; 20 | import java.util.stream.Collectors; 21 | 22 | /** 23 | * @author shadowfacts 24 | */ 25 | public class OneElevenTwoAdapter implements IMinecraftAdapter { 26 | 27 | @Override 28 | public void sendMessage(String message) { 29 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 30 | if (server != null) { 31 | server.getPlayerList().sendMessage(ForgeHooks.newChatWithLinks(message)); 32 | } 33 | } 34 | 35 | @Override 36 | public void sendMessageToPlayer(String message, String player) { 37 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 38 | EntityPlayer mcPlayer = server.getPlayerList().getPlayerByUsername(player); 39 | mcPlayer.sendMessage(ForgeHooks.newChatWithLinks(message)); 40 | } 41 | 42 | @Override 43 | public int[] getAllDimensions() { 44 | Integer[] boxed = DimensionManager.getIDs(); 45 | int[] unboxed = new int[boxed.length]; 46 | for (int i = 0; i < boxed.length; i++) { 47 | unboxed[i] = boxed[i]; 48 | } 49 | return unboxed; 50 | } 51 | 52 | @Override 53 | public double getTickTime(int dimension) { 54 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 55 | long[] tickTimes = server.worldTickTimes.get(dimension); 56 | long sum = 0; 57 | for (int i = 0; i < tickTimes.length; i++) { 58 | sum += tickTimes[i]; 59 | } 60 | return sum / tickTimes.length * 1.0E-6D; 61 | } 62 | 63 | @Override 64 | public Set getOnlinePlayers() { 65 | return FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().getPlayers().stream() 66 | .map(EntityPlayer::getName) 67 | .collect(Collectors.toSet()); 68 | } 69 | 70 | @Override 71 | public void executeCommand(String command) { 72 | FMLCommonHandler.instance().getMinecraftServerInstance().callFromMainThread(() -> { 73 | FMLCommonHandler.instance().getMinecraftServerInstance().getCommandManager().executeCommand(DummySender.INSTANCE, command); 74 | return null; 75 | }); 76 | } 77 | 78 | @Override 79 | public String teleportPlayerToSpawn(String username) { 80 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 81 | WorldServer world = server.worldServerForDimension(0); 82 | BlockPos spawn = world.getSpawnPoint(); 83 | PlayerList playerList = server.getPlayerList(); 84 | EntityPlayerMP player = playerList.getPlayerByUsername(username); 85 | if (player != null) { 86 | if (player.dimension != 0) { 87 | int original = player.dimension; 88 | playerList.transferPlayerToDimension(player, 0, new DummyTeleporter(world)); 89 | 90 | if (original == 1 && player.isEntityAlive()) { 91 | world.spawnEntity(player); 92 | world.updateEntityWithOptionalForce(player, false); 93 | } 94 | } 95 | player.setPositionAndUpdate(spawn.getX() + 0.5d, spawn.getY(), spawn.getZ() + 0.5d); 96 | } else { 97 | GameProfile profile = server.getPlayerProfileCache().getGameProfileForUsername(username); 98 | 99 | if (profile != null && profile.isComplete()) { 100 | FakePlayer fakePlayer = FakePlayerFactory.get(world, profile); 101 | IPlayerFileData saveHandler = world.getSaveHandler().getPlayerNBTManager(); 102 | NBTTagCompound tag = saveHandler.readPlayerData(fakePlayer); 103 | 104 | if (tag == null) { 105 | return "Unknown player: " + username; 106 | } 107 | 108 | fakePlayer.dimension = 0; 109 | fakePlayer.posX = spawn.getX() + 0.5d; 110 | fakePlayer.posY = spawn.getY(); 111 | fakePlayer.posZ = spawn.getZ() + 0.5d; 112 | 113 | saveHandler.writePlayerData(fakePlayer); 114 | } else { 115 | return "Unknown player: " + username; 116 | } 117 | } 118 | return null; 119 | } 120 | 121 | @Override 122 | public long getWorldTime(int dimension) { 123 | return FMLCommonHandler.instance().getMinecraftServerInstance().worldServerForDimension(dimension).getWorldTime(); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /1.11.2/src/main/java/net/shadowfacts/discordchat/one_eleven_two/OneElevenTwoMod.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eleven_two; 2 | 3 | import net.minecraftforge.common.MinecraftForge; 4 | import net.minecraftforge.fml.common.Mod; 5 | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; 6 | import net.minecraftforge.fml.common.event.FMLServerStartedEvent; 7 | import net.minecraftforge.fml.common.event.FMLServerStartingEvent; 8 | import net.minecraftforge.fml.common.event.FMLServerStoppedEvent; 9 | import net.shadowfacts.discordchat.api.IConfig; 10 | import net.shadowfacts.discordchat.api.IDiscordChat; 11 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 12 | import net.shadowfacts.discordchat.core.DiscordChat; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | 17 | /** 18 | * @author shadowfacts 19 | */ 20 | @Mod(modid = OneElevenTwoMod.MOD_ID, name = OneElevenTwoMod.NAME, version = OneElevenTwoMod.VERSION, acceptableRemoteVersions = "*") 21 | public class OneElevenTwoMod { 22 | 23 | public static final String MOD_ID = "discordchat"; 24 | public static final String NAME = "Discord Chat"; 25 | public static final String VERSION = "@VERSION@"; 26 | 27 | public static IMinecraftAdapter minecraftAdapter = new OneElevenTwoAdapter(); 28 | public static IConfig config; 29 | public static IDiscordChat discordChat; 30 | 31 | @Mod.EventHandler 32 | public void preInit(FMLPreInitializationEvent event) throws IOException { 33 | discordChat = new DiscordChat(new File(event.getModConfigurationDirectory(), "shadowfacts/DiscordChat/"), minecraftAdapter); 34 | config = discordChat.getConfig(); 35 | discordChat.connect(); 36 | 37 | MinecraftForge.EVENT_BUS.register(new ForgeEventHandler()); 38 | } 39 | 40 | @Mod.EventHandler 41 | public void serverStarting(FMLServerStartingEvent event) { 42 | event.registerServerCommand(new CommandDC(discordChat)); 43 | } 44 | 45 | @Mod.EventHandler 46 | public void serverStarted(FMLServerStartedEvent event) { 47 | discordChat.start(); 48 | if (config.sendServerOnlineOfflineMessages()) discordChat.sendMessage("Server is online"); 49 | } 50 | 51 | @Mod.EventHandler 52 | public void serverStopped(FMLServerStoppedEvent event) { 53 | if (config.sendServerOnlineOfflineMessages()) discordChat.sendMessage("Server is offline"); 54 | discordChat.stop(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /1.11.2/src/main/resources/assets/discordchat/default.conf: -------------------------------------------------------------------------------- 1 | discordchat { 2 | discord { 3 | token = "" 4 | server = "" 5 | channels = [] 6 | } 7 | 8 | commands { 9 | prefix = "!" 10 | 11 | permission { 12 | commands = "global" 13 | help = "global" 14 | reload = "admin" 15 | roleid = "global" 16 | exec = "admin" 17 | online = "global" 18 | tps = "global" 19 | unstick = "approved" 20 | time = "global" 21 | permission = "global" 22 | setPermission = "admin" 23 | tell = "global" 24 | } 25 | } 26 | 27 | relay { 28 | deaths = true 29 | achievements = true 30 | joinleave = true 31 | onlineoffline = true 32 | } 33 | 34 | filter { 35 | mode = "none" 36 | filter = "" 37 | stripFilter = false 38 | } 39 | 40 | format { 41 | # Default color if the role of the user has no color on Discord (eg. @everyone) 42 | defaultColor = "§f" 43 | 44 | # Format for a normal message from MC to Discord. 45 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 46 | fromMC = "MC » <$1> $2" 47 | 48 | # Format for a normal message from Discord to MC. 49 | # $1 will be replaced with the channel, $2 will be replaced with the sender's username, $3 will be replaced with the message, and $4 will be replaced with the MC color (e.g. §3) closest to the Discord user's color. 50 | fromDiscord = "#$1 » <$4$2§f> $3" 51 | 52 | # Format for a private message from MC to Discord. 53 | # $1 will be replaced with the sender's username and $2 will be replaced with the message. 54 | fromMCPrivate = "MC » <$1> $2" 55 | 56 | # Format for a private message from Discord to MC. 57 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 58 | fromDiscordPrivate = "Discord » <$1> $2" 59 | 60 | # Format for a player death message from MC to Discord. 61 | # $1 will be replaced with the player's username and $2 will be replaced with the death message 62 | death = "MC » $2" 63 | 64 | # Format for a player achievement message from MC to Discord. 65 | # $1 will be replaced with the player's username and $2 will be replaced with the achievement 66 | achievement = "MC » $1 has just earned the achievement $2" 67 | 68 | # Format for a player join message from MC to Discord. 69 | # $1 will be replaced with the player's username 70 | join = "MC » $1 joined the game" 71 | 72 | # Format for a player leave message from MC to Discord. 73 | # $1 will be replaced with the player's username 74 | leave = "MC » $1 left the game" 75 | 76 | # Format applied to every command response message. 77 | # Used for differentiating command responses from multiple bot instances. 78 | # $1 will be replaced with the message 79 | command = "$1" 80 | } 81 | } -------------------------------------------------------------------------------- /1.12.2/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | maven { 5 | name "forge" 6 | url "http://files.minecraftforge.net/maven/" 7 | } 8 | } 9 | dependencies { 10 | classpath "net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT" 11 | } 12 | } 13 | 14 | plugins { 15 | id "com.github.johnrengelman.shadow" version "1.2.3" 16 | } 17 | 18 | apply plugin: "net.minecraftforge.gradle.forge" 19 | 20 | sourceCompatibility = JavaVersion.VERSION_1_8 21 | targetCompatibility = JavaVersion.VERSION_1_8 22 | 23 | version = mc_version + "-" + mod_version 24 | 25 | archivesBaseName = "DiscordChat" 26 | 27 | minecraft { 28 | version = mc_version + "-" + forge_version 29 | runDir = "run" 30 | 31 | mappings = mcp_mappings 32 | 33 | replaceIn "OneTwelveTwoMod.java" 34 | replace "@VERSION@", mod_version 35 | } 36 | 37 | processResources { 38 | inputs.property "version", project.version 39 | inputs.property "mcversion", project.minecraft.version 40 | 41 | from (sourceSets.main.resources.srcDirs) { 42 | include "mcmod.info" 43 | expand "version": project.version, "mcversion": project.minecraft.version 44 | } 45 | from (sourceSets.main.resources.srcDirs) { 46 | exclude "mcmod.info" 47 | } 48 | } 49 | 50 | repositories { 51 | maven { 52 | name "shadowfacts" 53 | url "http://maven.shadowfacts.net/" 54 | } 55 | flatDir { 56 | dirs "../core/libs" 57 | } 58 | } 59 | 60 | dependencies { 61 | compile project(":core") 62 | } 63 | 64 | shadowJar { 65 | classifier = "" 66 | relocate "com.iwebpp.crypto", "net.shadowfacts.discordchat.repack.com.iwebpp.crypto" 67 | relocate "com.neovisionaries.ws.client", "net.shadowfacts.discordchat.repack.com.neovisionaries.ws.client" 68 | relocate "com.sun.jna", "net.shadowfacts.discordchat.repack.com.sun.jna" 69 | relocate "com.vdurmont.emoji", "net.shadowfacts.discordchat.repack.com.vdurmont.emoji" 70 | relocate "gnu.trove", "net.shadowfacts.discordchat.gnu.trove" 71 | relocate "net.dv8tion.jda", "net.shadowfacts.discordchat.repack.net.dv8tion.jda" 72 | relocate "okhttp3", "net.shadowfacts.discordchat.repack.okhttp3" 73 | relocate "okio", "net.shadowfacts.discordchat.repack.okio" 74 | relocate "org.apache.commons.collections4", "net.shadowfacts.discordchat.repack.org.apache.commons.collections4" 75 | relocate "org.apache.commons.lang3", "net.shadowfacts.discordchat.repack.org.apache.commons.lang3" 76 | relocate "org.json", "net.shadowfacts.discordchat.repack.org.json" 77 | relocate "tomp2p.opuswrapper", "net.shadowfacts.discordchat.repack.tomp2p.opuswrapper" 78 | 79 | dependencies { 80 | include(project(":core")) 81 | include(dependency("JDA:JDA:3.3.0_260:withDependencies")) 82 | include(dependency("com.vdurmont:emoji-java:3.1.3")) 83 | include(dependency("net.shadowfacts:ShadowLib:1.9.0")) 84 | } 85 | } 86 | 87 | reobf { shadowJar { mappingType = "SEARGE" } } 88 | tasks.reobfShadowJar.mustRunAfter shadowJar 89 | -------------------------------------------------------------------------------- /1.12.2/gradle.properties: -------------------------------------------------------------------------------- 1 | group = net.shadowfacts 2 | mc_version = 1.12.2 3 | mcp_mappings = snapshot_20171008 4 | forge_version = 14.23.0.2509 -------------------------------------------------------------------------------- /1.12.2/src/main/java/net/shadowfacts/discordchat/one_twelve_two/CommandDC.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_twelve_two; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.dv8tion.jda.core.entities.Role; 5 | import net.minecraft.command.CommandBase; 6 | import net.minecraft.command.CommandException; 7 | import net.minecraft.command.ICommandSender; 8 | import net.minecraft.command.WrongUsageException; 9 | import net.minecraft.server.MinecraftServer; 10 | import net.minecraft.util.text.TextComponentString; 11 | import net.minecraft.util.text.TextComponentTranslation; 12 | import net.shadowfacts.discordchat.api.IConfig; 13 | import net.shadowfacts.discordchat.api.IDiscordChat; 14 | import net.shadowfacts.discordchat.api.ILogger; 15 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 16 | import net.shadowfacts.discordchat.api.permission.Permission; 17 | 18 | import java.io.IOException; 19 | import java.util.Arrays; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.function.BiConsumer; 24 | 25 | /** 26 | * @author shadowfacts 27 | */ 28 | public class CommandDC extends CommandBase { 29 | 30 | private IDiscordChat discordChat; 31 | private ILogger logger; 32 | private IConfig config; 33 | private IPermissionManager permissionManager; 34 | 35 | private Map> subcommands = new HashMap<>(); 36 | 37 | public CommandDC(IDiscordChat discordChat) { 38 | this.discordChat = discordChat; 39 | logger = discordChat.getLogger(); 40 | config = discordChat.getConfig(); 41 | permissionManager = discordChat.getPermissionManager(); 42 | 43 | subcommands.put("setpermission", this::setPermission); 44 | subcommands.put("tell", this::tell); 45 | } 46 | 47 | @Override 48 | public String getName() { 49 | return "discordchat"; 50 | } 51 | 52 | @Override 53 | public String getUsage(ICommandSender sender) { 54 | return "/discordchat "; 55 | } 56 | 57 | @Override 58 | public List getAliases() { 59 | return ImmutableList.of("dc"); 60 | } 61 | 62 | @Override 63 | public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { 64 | if (args.length < 1) { 65 | throw new WrongUsageException(getUsage(sender)); 66 | } 67 | 68 | String command = args[0].toLowerCase(); 69 | args = Arrays.copyOfRange(args, 1, args.length); 70 | 71 | if (!subcommands.containsKey(command)) { 72 | throw new WrongUsageException(getUsage(sender)); 73 | } 74 | 75 | subcommands.get(command).accept(sender, args); 76 | } 77 | 78 | private void setPermission(ICommandSender sender, String[] args) { 79 | if (args.length < 2) { 80 | wrongUsage(sender, "/discordchat setPermission "); 81 | } 82 | 83 | Permission permission = Permission.valueOf(args[args.length - 1].toUpperCase()); 84 | 85 | String role = String.join(" ", Arrays.copyOfRange(args, 0, args.length - 1)); 86 | List roles = discordChat.getJDA().getGuildById(config.getServerID()).getRolesByName(role, true); 87 | if (roles.isEmpty()) { 88 | sender.sendMessage(new TextComponentString("No such role: " + role)); 89 | return; 90 | } 91 | 92 | permissionManager.set(roles.get(0), permission); 93 | 94 | try { 95 | permissionManager.save(); 96 | sender.sendMessage(new TextComponentString("Permissions updated")); 97 | } catch (IOException e) { 98 | logger.error(e, "Unable to save permissions"); 99 | } 100 | } 101 | 102 | private void tell(ICommandSender sender, String[] args) { 103 | if (args.length < 2) { 104 | wrongUsage(sender, "/discordchat tell "); 105 | } else { 106 | String user = args[0]; 107 | String message = ""; 108 | for (int i = 1; i < args.length; i++) { 109 | message += args[i]; 110 | if (i != args.length - 1) message += " "; 111 | } 112 | discordChat.sendPrivateMessage(sender.getName(), message, user); 113 | } 114 | } 115 | 116 | private void wrongUsage(ICommandSender sender, String usage) { 117 | sender.sendMessage(new TextComponentTranslation("commands.generic.usage", usage)); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /1.12.2/src/main/java/net/shadowfacts/discordchat/one_twelve_two/DummySender.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_twelve_two; 2 | 3 | import net.minecraft.command.CommandResultStats; 4 | import net.minecraft.command.ICommandSender; 5 | import net.minecraft.entity.Entity; 6 | import net.minecraft.server.MinecraftServer; 7 | import net.minecraft.util.math.BlockPos; 8 | import net.minecraft.util.math.Vec3d; 9 | import net.minecraft.util.text.ITextComponent; 10 | import net.minecraft.util.text.TextComponentString; 11 | import net.minecraft.world.World; 12 | import net.minecraftforge.fml.common.FMLCommonHandler; 13 | 14 | import javax.annotation.Nullable; 15 | 16 | /** 17 | * @author shadowfacts 18 | */ 19 | public class DummySender implements ICommandSender { 20 | 21 | public static final ICommandSender INSTANCE = new DummySender(); 22 | 23 | @Override 24 | public String getName() { 25 | return "[DiscordChat]"; 26 | } 27 | 28 | @Override 29 | public ITextComponent getDisplayName() { 30 | return new TextComponentString(getName()); 31 | } 32 | 33 | @Override 34 | public void sendMessage(ITextComponent component) { 35 | OneTwelveTwoMod.discordChat.sendMessage(OneTwelveTwoMod.discordChat.getFormatter().command("```" + component.getUnformattedText() + "```")); 36 | } 37 | 38 | @Override 39 | public boolean canUseCommand(int permLevel, String commandName) { 40 | return true; 41 | } 42 | 43 | @Override 44 | public BlockPos getPosition() { 45 | return BlockPos.ORIGIN; 46 | } 47 | 48 | @Override 49 | public Vec3d getPositionVector() { 50 | return Vec3d.ZERO; 51 | } 52 | 53 | @Override 54 | public World getEntityWorld() { 55 | return getServer().getWorld(0); 56 | } 57 | 58 | @Nullable 59 | @Override 60 | public Entity getCommandSenderEntity() { 61 | return null; 62 | } 63 | 64 | @Override 65 | public boolean sendCommandFeedback() { 66 | return false; 67 | } 68 | 69 | @Override 70 | public void setCommandStat(CommandResultStats.Type type, int amount) { 71 | 72 | } 73 | 74 | @Nullable 75 | @Override 76 | public MinecraftServer getServer() { 77 | return FMLCommonHandler.instance().getMinecraftServerInstance(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /1.12.2/src/main/java/net/shadowfacts/discordchat/one_twelve_two/DummyTeleporter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_twelve_two; 2 | 3 | import net.minecraft.entity.Entity; 4 | import net.minecraft.world.Teleporter; 5 | import net.minecraft.world.WorldServer; 6 | 7 | /** 8 | * @author shadowfacts 9 | */ 10 | public class DummyTeleporter extends Teleporter { 11 | 12 | public DummyTeleporter(WorldServer world) { 13 | super(world); 14 | } 15 | 16 | @Override 17 | public void placeInPortal(Entity entity, float rotationYaw) { 18 | 19 | } 20 | 21 | @Override 22 | public boolean placeInExistingPortal(Entity entity, float rotationYaw) { 23 | entity.motionX = 0; 24 | entity.motionY = 0; 25 | entity.motionZ = 0; 26 | entity.fallDistance = 0; 27 | return true; 28 | } 29 | 30 | @Override 31 | public boolean makePortal(Entity entity) { 32 | return true; 33 | } 34 | 35 | @Override 36 | public void removeStalePortalLocations(long worldTime) { 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /1.12.2/src/main/java/net/shadowfacts/discordchat/one_twelve_two/ForgeEventHandler.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_twelve_two; 2 | 3 | import net.minecraft.entity.player.EntityPlayer; 4 | import net.minecraftforge.common.util.FakePlayer; 5 | import net.minecraftforge.event.ServerChatEvent; 6 | import net.minecraftforge.event.entity.living.LivingDeathEvent; 7 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 8 | import net.minecraftforge.fml.common.gameevent.PlayerEvent; 9 | 10 | /** 11 | * @author shadowfacts 12 | */ 13 | public class ForgeEventHandler { 14 | 15 | @SubscribeEvent 16 | public void onServerChat(ServerChatEvent event) { 17 | String message = OneTwelveTwoMod.discordChat.filterMCMessage(event.getMessage()); 18 | if (message != null) { 19 | OneTwelveTwoMod.discordChat.sendMessage(OneTwelveTwoMod.discordChat.getFormatter().fromMC(event.getPlayer().getName(), message)); 20 | } 21 | } 22 | 23 | @SubscribeEvent 24 | public void onLivingDeath(LivingDeathEvent event) { 25 | if (OneTwelveTwoMod.config.sendDeathMessages() && event.getEntityLiving() instanceof EntityPlayer && !(event.getEntityLiving() instanceof FakePlayer) && !event.getEntity().world.isRemote) { 26 | EntityPlayer player = (EntityPlayer)event.getEntityLiving(); 27 | OneTwelveTwoMod.discordChat.sendMessage(OneTwelveTwoMod.discordChat.getFormatter().death(player.getName(), player.getCombatTracker().getDeathMessage().getUnformattedText())); 28 | } 29 | } 30 | 31 | @SubscribeEvent 32 | public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { 33 | if (OneTwelveTwoMod.config.sendJoinLeaveMessages()) { 34 | OneTwelveTwoMod.discordChat.sendMessage(OneTwelveTwoMod.discordChat.getFormatter().join(event.player.getName())); 35 | } 36 | } 37 | 38 | @SubscribeEvent 39 | public void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { 40 | if (OneTwelveTwoMod.config.sendJoinLeaveMessages()) { 41 | OneTwelveTwoMod.discordChat.sendMessage(OneTwelveTwoMod.discordChat.getFormatter().leave(event.player.getName())); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /1.12.2/src/main/java/net/shadowfacts/discordchat/one_twelve_two/OneTwelveTwoAdapter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_twelve_two; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import net.minecraft.entity.player.EntityPlayer; 5 | import net.minecraft.entity.player.EntityPlayerMP; 6 | import net.minecraft.nbt.NBTTagCompound; 7 | import net.minecraft.server.MinecraftServer; 8 | import net.minecraft.server.management.PlayerList; 9 | import net.minecraft.util.math.BlockPos; 10 | import net.minecraft.world.WorldServer; 11 | import net.minecraft.world.storage.IPlayerFileData; 12 | import net.minecraftforge.common.DimensionManager; 13 | import net.minecraftforge.common.ForgeHooks; 14 | import net.minecraftforge.common.util.FakePlayer; 15 | import net.minecraftforge.common.util.FakePlayerFactory; 16 | import net.minecraftforge.fml.common.FMLCommonHandler; 17 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 18 | 19 | import java.util.Set; 20 | import java.util.stream.Collectors; 21 | 22 | /** 23 | * @author shadowfacts 24 | */ 25 | public class OneTwelveTwoAdapter implements IMinecraftAdapter { 26 | 27 | @Override 28 | public void sendMessage(String message) { 29 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 30 | if (server != null) { 31 | server.getPlayerList().sendMessage(ForgeHooks.newChatWithLinks(message)); 32 | } 33 | } 34 | 35 | @Override 36 | public void sendMessageToPlayer(String message, String player) { 37 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 38 | EntityPlayer mcPlayer = server.getPlayerList().getPlayerByUsername(player); 39 | mcPlayer.sendMessage(ForgeHooks.newChatWithLinks(message)); 40 | } 41 | 42 | @Override 43 | public int[] getAllDimensions() { 44 | Integer[] boxed = DimensionManager.getIDs(); 45 | int[] unboxed = new int[boxed.length]; 46 | for (int i = 0; i < boxed.length; i++) { 47 | unboxed[i] = boxed[i]; 48 | } 49 | return unboxed; 50 | } 51 | 52 | @Override 53 | public double getTickTime(int dimension) { 54 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 55 | long[] tickTimes = server.worldTickTimes.get(dimension); 56 | long sum = 0; 57 | for (int i = 0; i < tickTimes.length; i++) { 58 | sum += tickTimes[i]; 59 | } 60 | return sum / tickTimes.length * 1.0E-6D; 61 | } 62 | 63 | @Override 64 | public Set getOnlinePlayers() { 65 | return FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().getPlayers().stream() 66 | .map(EntityPlayer::getName) 67 | .collect(Collectors.toSet()); 68 | } 69 | 70 | @Override 71 | public void executeCommand(String command) { 72 | FMLCommonHandler.instance().getMinecraftServerInstance().callFromMainThread(() -> { 73 | FMLCommonHandler.instance().getMinecraftServerInstance().getCommandManager().executeCommand(DummySender.INSTANCE, command); 74 | return null; 75 | }); 76 | } 77 | 78 | @Override 79 | public String teleportPlayerToSpawn(String username) { 80 | MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); 81 | WorldServer world = server.getWorld(0); 82 | BlockPos spawn = world.getSpawnPoint(); 83 | PlayerList playerList = server.getPlayerList(); 84 | EntityPlayerMP player = playerList.getPlayerByUsername(username); 85 | if (player != null) { 86 | if (player.dimension != 0) { 87 | int original = player.dimension; 88 | playerList.transferPlayerToDimension(player, 0, new DummyTeleporter(world)); 89 | 90 | if (original == 1 && player.isEntityAlive()) { 91 | world.spawnEntity(player); 92 | world.updateEntityWithOptionalForce(player, false); 93 | } 94 | } 95 | player.setPositionAndUpdate(spawn.getX() + 0.5d, spawn.getY(), spawn.getZ() + 0.5d); 96 | } else { 97 | GameProfile profile = server.getPlayerProfileCache().getGameProfileForUsername(username); 98 | 99 | if (profile != null && profile.isComplete()) { 100 | FakePlayer fakePlayer = FakePlayerFactory.get(world, profile); 101 | IPlayerFileData saveHandler = world.getSaveHandler().getPlayerNBTManager(); 102 | NBTTagCompound tag = saveHandler.readPlayerData(fakePlayer); 103 | 104 | if (tag == null) { 105 | return "Unknown player: " + username; 106 | } 107 | 108 | fakePlayer.dimension = 0; 109 | fakePlayer.posX = spawn.getX() + 0.5d; 110 | fakePlayer.posY = spawn.getY(); 111 | fakePlayer.posZ = spawn.getZ() + 0.5d; 112 | 113 | saveHandler.writePlayerData(fakePlayer); 114 | } else { 115 | return "Unknown player: " + username; 116 | } 117 | } 118 | return null; 119 | } 120 | 121 | @Override 122 | public long getWorldTime(int dimension) { 123 | return FMLCommonHandler.instance().getMinecraftServerInstance().getWorld(dimension).getWorldTime(); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /1.12.2/src/main/java/net/shadowfacts/discordchat/one_twelve_two/OneTwelveTwoMod.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_twelve_two; 2 | 3 | import net.minecraftforge.common.MinecraftForge; 4 | import net.minecraftforge.fml.common.Mod; 5 | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; 6 | import net.minecraftforge.fml.common.event.FMLServerStartedEvent; 7 | import net.minecraftforge.fml.common.event.FMLServerStartingEvent; 8 | import net.minecraftforge.fml.common.event.FMLServerStoppedEvent; 9 | import net.shadowfacts.discordchat.api.IConfig; 10 | import net.shadowfacts.discordchat.api.IDiscordChat; 11 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 12 | import net.shadowfacts.discordchat.core.DiscordChat; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | 17 | /** 18 | * @author shadowfacts 19 | */ 20 | @Mod(modid = OneTwelveTwoMod.MOD_ID, name = OneTwelveTwoMod.NAME, version = OneTwelveTwoMod.VERSION, acceptableRemoteVersions = "*") 21 | public class OneTwelveTwoMod { 22 | 23 | public static final String MOD_ID = "discordchat"; 24 | public static final String NAME = "Discord Chat"; 25 | public static final String VERSION = "@VERSION@"; 26 | 27 | public static IMinecraftAdapter minecraftAdapter = new OneTwelveTwoAdapter(); 28 | public static IConfig config; 29 | public static IDiscordChat discordChat; 30 | 31 | @Mod.EventHandler 32 | public void preInit(FMLPreInitializationEvent event) throws IOException { 33 | discordChat = new DiscordChat(new File(event.getModConfigurationDirectory(), "shadowfacts/DiscordChat/"), minecraftAdapter); 34 | config = discordChat.getConfig(); 35 | discordChat.connect(); 36 | 37 | MinecraftForge.EVENT_BUS.register(new ForgeEventHandler()); 38 | } 39 | 40 | @Mod.EventHandler 41 | public void serverStarting(FMLServerStartingEvent event) { 42 | event.registerServerCommand(new CommandDC(discordChat)); 43 | } 44 | 45 | @Mod.EventHandler 46 | public void serverStarted(FMLServerStartedEvent event) { 47 | discordChat.start(); 48 | if (config.sendServerOnlineOfflineMessages()) discordChat.sendMessage("Server is online"); 49 | } 50 | 51 | @Mod.EventHandler 52 | public void serverStopped(FMLServerStoppedEvent event) { 53 | if (config.sendServerOnlineOfflineMessages()) discordChat.sendMessage("Server is offline"); 54 | discordChat.stop(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /1.12.2/src/main/resources/assets/discordchat/default.conf: -------------------------------------------------------------------------------- 1 | discordchat { 2 | discord { 3 | token = "" 4 | server = "" 5 | channels = [] 6 | } 7 | 8 | commands { 9 | prefix = "!" 10 | 11 | permission { 12 | commands = "global" 13 | help = "global" 14 | reload = "admin" 15 | roleid = "global" 16 | exec = "admin" 17 | online = "global" 18 | tps = "global" 19 | unstick = "approved" 20 | time = "global" 21 | permission = "global" 22 | setPermission = "admin" 23 | tell = "global" 24 | } 25 | } 26 | 27 | relay { 28 | deaths = true 29 | achievements = true 30 | joinleave = true 31 | onlineoffline = true 32 | } 33 | 34 | filter { 35 | mode = "none" 36 | filter = "" 37 | stripFilter = false 38 | } 39 | 40 | format { 41 | # Default color if the role of the user has no color on Discord (eg. @everyone) 42 | defaultColor = "§f" 43 | 44 | # Format for a normal message from MC to Discord. 45 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 46 | fromMC = "MC » <$1> $2" 47 | 48 | # Format for a normal message from Discord to MC. 49 | # $1 will be replaced with the channel, $2 will be replaced with the sender's username, $3 will be replaced with the message, and $4 will be replaced with the MC color (e.g. §3) closest to the Discord user's color. 50 | fromDiscord = "#$1 » <$4$2§f> $3" 51 | 52 | # Format for a private message from MC to Discord. 53 | # $1 will be replaced with the sender's username and $2 will be replaced with the message. 54 | fromMCPrivate = "MC » <$1> $2" 55 | 56 | # Format for a private message from Discord to MC. 57 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 58 | fromDiscordPrivate = "Discord » <$1> $2" 59 | 60 | # Format for a player death message from MC to Discord. 61 | # $1 will be replaced with the player's username and $2 will be replaced with the death message 62 | death = "MC » $2" 63 | 64 | # Format for a player achievement message from MC to Discord. 65 | # $1 will be replaced with the player's username and $2 will be replaced with the achievement 66 | achievement = "MC » $1 has just earned the achievement $2" 67 | 68 | # Format for a player join message from MC to Discord. 69 | # $1 will be replaced with the player's username 70 | join = "MC » $1 joined the game" 71 | 72 | # Format for a player leave message from MC to Discord. 73 | # $1 will be replaced with the player's username 74 | leave = "MC » $1 left the game" 75 | 76 | # Format applied to every command response message. 77 | # Used for differentiating command responses from multiple bot instances. 78 | # $1 will be replaced with the message 79 | command = "$1" 80 | } 81 | } -------------------------------------------------------------------------------- /1.7.10/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | name "forge" 6 | url "http://files.minecraftforge.net/maven/" 7 | } 8 | maven { 9 | name "sonatype" 10 | url "https://oss.sonatype.org/content/repositories/snapshots" 11 | } 12 | } 13 | dependencies { 14 | classpath "net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT" 15 | } 16 | } 17 | 18 | apply plugin: "forge" 19 | 20 | sourceCompatibility = JavaVersion.VERSION_1_8 21 | targetCompatibility = JavaVersion.VERSION_1_8 22 | 23 | version = mc_version + "-" + mod_version 24 | 25 | archivesBaseName = "DiscordChat" 26 | 27 | minecraft { 28 | version = mc_version + "-" + forge_version 29 | runDir = "run" 30 | 31 | mappings = mcp_mappings 32 | 33 | replaceIn "OneSevenTenMod.java" 34 | replace "@VERSION@", mod_version 35 | 36 | srgExtra "PK: com/iwebpp/crypto net/shadowfacts/discordchat/repack/com/iwebpp/crypto" 37 | srgExtra "PK: com/neovisionaries/ws/client net/shadowfacts/discordchat/repack/com/neovisionaries/ws/client" 38 | srgExtra "PK: com/sun/jna net/shadowfacts/discordchat/repack/com/sun/jna" 39 | srgExtra "PK: com/vdurmont/emoji net/shadowfacts/discordchat/repack/com/vdurmont/emoji" 40 | srgExtra "PK: gnu/trove net/shadowfacts/discordchat/repack/gnu/trove" 41 | srgExtra "PK: net/dv8tion/jda net/shadowfacts/discordchat/repack/net/dv8tion/jda" 42 | srgExtra "PK: okhttp3 net/shadowfacts/discordchat/repack/okhttp3" 43 | srgExtra "PK: okio net/shadowfacts/discordchat/repack/okio" 44 | srgExtra "PK: org/apache/commons/collections4 net/shadowfacts/discordchat/repack/org/apache/commons/collections4" 45 | srgExtra "PK: org/apache/commons/lang3 net/shadowfacts/discordchat/repack/org/apache/commons/lang3" 46 | srgExtra "PK: org/json net/shadowfacts/discordchat/repack/org/json" 47 | srgExtra "PK: tomp2p/opuswrapper net/shadowfacts/discordchat/repack/tomp2p/opuswrapper" 48 | } 49 | 50 | processResources { 51 | inputs.property "version", project.version 52 | inputs.property "mcversion", project.minecraft.version 53 | 54 | from (sourceSets.main.resources.srcDirs) { 55 | include "mcmod.info" 56 | expand "version": project.version, "mcversion": project.minecraft.version 57 | } 58 | from (sourceSets.main.resources.srcDirs) { 59 | exclude "mcmod.info" 60 | } 61 | } 62 | 63 | configurations { 64 | shade 65 | compile.extendsFrom shade 66 | } 67 | 68 | repositories { 69 | maven { 70 | name "shadowfacts" 71 | url "http://maven.shadowfacts.net/" 72 | } 73 | flatDir { 74 | dirs "../core/libs" 75 | } 76 | } 77 | 78 | dependencies { 79 | shade project(":core") 80 | } 81 | 82 | jar { 83 | configurations.shade.each { dep -> 84 | from (project.zipTree(dep)) { 85 | exclude "META-INF", "META-INF/**" 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /1.7.10/gradle.properties: -------------------------------------------------------------------------------- 1 | group = net.shadowfacts 2 | mc_version = 1.7.10 3 | mcp_mappings = stable_12 4 | forge_version = 10.13.4.1614-1.7.10 -------------------------------------------------------------------------------- /1.7.10/src/main/java/net/shadowfacts/discordchat/one_seven_ten/CommandDC.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_seven_ten; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.dv8tion.jda.core.entities.Role; 5 | import net.minecraft.command.CommandBase; 6 | import net.minecraft.command.CommandException; 7 | import net.minecraft.command.ICommandSender; 8 | import net.minecraft.command.WrongUsageException; 9 | import net.minecraft.util.ChatComponentText; 10 | import net.minecraft.util.ChatComponentTranslation; 11 | import net.shadowfacts.discordchat.api.IConfig; 12 | import net.shadowfacts.discordchat.api.IDiscordChat; 13 | import net.shadowfacts.discordchat.api.ILogger; 14 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 15 | import net.shadowfacts.discordchat.api.permission.Permission; 16 | 17 | import java.io.IOException; 18 | import java.util.Arrays; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.function.BiConsumer; 23 | 24 | /** 25 | * @author shadowfacts 26 | */ 27 | public class CommandDC extends CommandBase { 28 | 29 | private IDiscordChat discordChat; 30 | private ILogger logger; 31 | private IConfig config; 32 | private IPermissionManager permissionManager; 33 | 34 | private Map> subcommands = new HashMap<>(); 35 | 36 | public CommandDC(IDiscordChat discordChat) { 37 | this.discordChat = discordChat; 38 | logger = discordChat.getLogger(); 39 | config = discordChat.getConfig(); 40 | permissionManager = discordChat.getPermissionManager(); 41 | 42 | subcommands.put("setpermission", this::setPermission); 43 | subcommands.put("tell", this::tell); 44 | } 45 | 46 | @Override 47 | public String getCommandName() { 48 | return "discordchat"; 49 | } 50 | 51 | @Override 52 | public String getCommandUsage(ICommandSender sender) { 53 | return "/discordchat "; 54 | } 55 | 56 | @Override 57 | public List getCommandAliases() { 58 | return ImmutableList.of("dc"); 59 | } 60 | 61 | @Override 62 | public void processCommand(ICommandSender sender, String[] args) throws CommandException { 63 | if (args.length < 1) { 64 | throw new WrongUsageException(getCommandUsage(sender)); 65 | } 66 | 67 | String command = args[0].toLowerCase(); 68 | args = Arrays.copyOfRange(args, 1, args.length); 69 | 70 | if (!subcommands.containsKey(command)) { 71 | throw new WrongUsageException(getCommandUsage(sender)); 72 | } 73 | 74 | subcommands.get(command).accept(sender, args); 75 | } 76 | 77 | private void setPermission(ICommandSender sender, String[] args) { 78 | if (args.length < 2) { 79 | wrongUsage(sender, "/discordchat setPermission "); 80 | } 81 | 82 | Permission permission = Permission.valueOf(args[args.length - 1].toUpperCase()); 83 | 84 | String role = String.join(" ", Arrays.copyOfRange(args, 0, args.length - 1)); 85 | List roles = discordChat.getJDA().getGuildById(config.getServerID()).getRolesByName(role, true); 86 | if (roles.isEmpty()) { 87 | sender.addChatMessage(new ChatComponentText("No such role: " + role)); 88 | return; 89 | } 90 | 91 | permissionManager.set(roles.get(0), permission); 92 | 93 | try { 94 | permissionManager.save(); 95 | sender.addChatMessage(new ChatComponentText("Permissions updated")); 96 | } catch (IOException e) { 97 | logger.error(e, "Unable to save permissions"); 98 | } 99 | } 100 | 101 | private void tell(ICommandSender sender, String[] args) { 102 | if (args.length < 2) { 103 | wrongUsage(sender, "/discordchat tell "); 104 | } else { 105 | String user = args[0]; 106 | String message = ""; 107 | for (int i = 1; i < args.length; i++) { 108 | message += args[i]; 109 | if (i != args.length - 1) message += " "; 110 | } 111 | discordChat.sendPrivateMessage(sender.getCommandSenderName(), message, user); 112 | } 113 | } 114 | 115 | private void wrongUsage(ICommandSender sender, String usage) { 116 | sender.addChatMessage(new ChatComponentTranslation("commands.generic.usage", usage)); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /1.7.10/src/main/java/net/shadowfacts/discordchat/one_seven_ten/DummySender.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_seven_ten; 2 | 3 | import net.minecraft.command.ICommandSender; 4 | import net.minecraft.server.MinecraftServer; 5 | import net.minecraft.util.ChatComponentText; 6 | import net.minecraft.util.ChunkCoordinates; 7 | import net.minecraft.util.IChatComponent; 8 | import net.minecraft.world.World; 9 | 10 | /** 11 | * @author shadowfacts 12 | */ 13 | public class DummySender implements ICommandSender { 14 | 15 | public static final ICommandSender INSTANCE = new DummySender(); 16 | 17 | @Override 18 | public String getCommandSenderName() { 19 | return "[DiscordChat]"; 20 | } 21 | 22 | @Override 23 | public IChatComponent getFormattedCommandSenderName() { 24 | return new ChatComponentText(getCommandSenderName()); 25 | } 26 | 27 | 28 | @Override 29 | public void addChatMessage(IChatComponent component) { 30 | OneSevenTenMod.discordChat.sendMessage(OneSevenTenMod.discordChat.getFormatter().command("```" + component.getUnformattedText() + "```")); 31 | } 32 | 33 | @Override 34 | public boolean canCommandSenderUseCommand(int permLevel, String commandName) { 35 | return true; 36 | } 37 | 38 | @Override 39 | public ChunkCoordinates getCommandSenderPosition() { 40 | return new ChunkCoordinates(0, 0, 0); 41 | } 42 | 43 | @Override 44 | public World getEntityWorld() { 45 | return MinecraftServer.getServer().worldServerForDimension(0); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /1.7.10/src/main/java/net/shadowfacts/discordchat/one_seven_ten/DummyTeleporter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_seven_ten; 2 | 3 | import net.minecraft.entity.Entity; 4 | import net.minecraft.world.Teleporter; 5 | import net.minecraft.world.WorldServer; 6 | 7 | /** 8 | * @author shadowfacts 9 | */ 10 | public class DummyTeleporter extends Teleporter { 11 | 12 | public DummyTeleporter(WorldServer world) { 13 | super(world); 14 | } 15 | 16 | @Override 17 | public void placeInPortal(Entity entity, double p_77185_2_, double p_77185_4_, double p_77185_6_, float p_77185_8_) { 18 | entity.motionX = 0; 19 | entity.motionY = 0; 20 | entity.motionZ = 0; 21 | entity.fallDistance = 0; 22 | } 23 | 24 | @Override 25 | public boolean placeInExistingPortal(Entity entity, double p_77184_2_, double p_77184_4_, double p_77184_6_, float p_77184_8_) { 26 | entity.motionX = 0; 27 | entity.motionY = 0; 28 | entity.motionZ = 0; 29 | entity.fallDistance = 0; 30 | return true; 31 | } 32 | 33 | @Override 34 | public boolean makePortal(Entity entity) { 35 | return true; 36 | } 37 | 38 | @Override 39 | public void removeStalePortalLocations(long worldTime) { 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /1.7.10/src/main/java/net/shadowfacts/discordchat/one_seven_ten/FMLEventHandler.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_seven_ten; 2 | 3 | import cpw.mods.fml.common.eventhandler.SubscribeEvent; 4 | import cpw.mods.fml.common.gameevent.PlayerEvent; 5 | 6 | /** 7 | * @author shadowfacts 8 | */ 9 | public class FMLEventHandler { 10 | 11 | @SubscribeEvent 12 | public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { 13 | if (OneSevenTenMod.config.sendJoinLeaveMessages()) { 14 | OneSevenTenMod.discordChat.sendMessage(OneSevenTenMod.discordChat.getFormatter().join(event.player.getDisplayName())); 15 | } 16 | } 17 | 18 | @SubscribeEvent 19 | public void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { 20 | if (OneSevenTenMod.config.sendJoinLeaveMessages()) { 21 | OneSevenTenMod.discordChat.sendMessage(OneSevenTenMod.discordChat.getFormatter().leave(event.player.getDisplayName())); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /1.7.10/src/main/java/net/shadowfacts/discordchat/one_seven_ten/ForgeEventHandler.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_seven_ten; 2 | 3 | import cpw.mods.fml.common.eventhandler.SubscribeEvent; 4 | import net.minecraft.entity.player.EntityPlayer; 5 | import net.minecraft.entity.player.EntityPlayerMP; 6 | import net.minecraft.util.ChatComponentText; 7 | import net.minecraft.util.IChatComponent; 8 | import net.minecraftforge.common.util.FakePlayer; 9 | import net.minecraftforge.event.ServerChatEvent; 10 | import net.minecraftforge.event.entity.living.LivingDeathEvent; 11 | import net.minecraftforge.event.entity.player.AchievementEvent; 12 | 13 | /** 14 | * @author shadowfacts 15 | */ 16 | public class ForgeEventHandler { 17 | 18 | @SubscribeEvent 19 | public void onServerChat(ServerChatEvent event) { 20 | String message = OneSevenTenMod.discordChat.filterMCMessage(event.message); 21 | if (message != null) { 22 | OneSevenTenMod.discordChat.sendMessage(OneSevenTenMod.discordChat.getFormatter().fromMC(event.player.getDisplayName(), message)); 23 | } 24 | } 25 | 26 | @SubscribeEvent 27 | public void onLivingDeath(LivingDeathEvent event) { 28 | if (OneSevenTenMod.config.sendDeathMessages() && event.entityLiving instanceof EntityPlayer && !(event.entityLiving instanceof FakePlayer)) { 29 | EntityPlayer player = (EntityPlayer)event.entityLiving; 30 | OneSevenTenMod.discordChat.sendMessage(OneSevenTenMod.discordChat.getFormatter().death(player.getDisplayName(), player.getCombatTracker().func_151521_b().getUnformattedText())); 31 | } 32 | } 33 | 34 | @SubscribeEvent 35 | public void onPlayerReceiveAchievement(AchievementEvent event) { 36 | if (OneSevenTenMod.config.sendAchievementMessages() && event.entityPlayer instanceof EntityPlayerMP) { 37 | if (((EntityPlayerMP) event.entityPlayer).getStatFile().hasAchievementUnlocked(event.achievement)) { 38 | return; 39 | } 40 | if (!((EntityPlayerMP) event.entityPlayer).getStatFile().canUnlockAchievement(event.achievement)) { 41 | return; 42 | } 43 | IChatComponent achievementComponent = event.achievement.getStatName(); 44 | IChatComponent achievementText = new ChatComponentText("[").appendSibling(achievementComponent).appendText("]"); 45 | OneSevenTenMod.discordChat.sendMessage(OneSevenTenMod.discordChat.getFormatter().achievement(event.entityPlayer.getDisplayName(), achievementText.getUnformattedText())); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /1.7.10/src/main/java/net/shadowfacts/discordchat/one_seven_ten/OneSevenTenAdapter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_seven_ten; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import net.minecraft.entity.player.EntityPlayer; 5 | import net.minecraft.entity.player.EntityPlayerMP; 6 | import net.minecraft.nbt.NBTTagCompound; 7 | import net.minecraft.server.MinecraftServer; 8 | import net.minecraft.server.management.ServerConfigurationManager; 9 | import net.minecraft.util.ChatComponentText; 10 | import net.minecraft.util.ChunkCoordinates; 11 | import net.minecraft.world.WorldServer; 12 | import net.minecraft.world.storage.IPlayerFileData; 13 | import net.minecraftforge.common.DimensionManager; 14 | import net.minecraftforge.common.ForgeHooks; 15 | import net.minecraftforge.common.util.FakePlayer; 16 | import net.minecraftforge.common.util.FakePlayerFactory; 17 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 18 | 19 | import java.util.Set; 20 | import java.util.stream.Collectors; 21 | 22 | /** 23 | * @author shadowfacts 24 | */ 25 | public class OneSevenTenAdapter implements IMinecraftAdapter { 26 | 27 | @Override 28 | public void sendMessage(String message) { 29 | MinecraftServer server = MinecraftServer.getServer(); 30 | if (server != null) { 31 | server.getConfigurationManager().sendChatMsg(ForgeHooks.newChatWithLinks(message)); 32 | } 33 | } 34 | 35 | @Override 36 | public void sendMessageToPlayer(String message, String player) { 37 | MinecraftServer server = MinecraftServer.getServer(); 38 | EntityPlayer mcPlayer = server.getConfigurationManager().getPlayerByUsername(player); 39 | mcPlayer.addChatMessage(ForgeHooks.newChatWithLinks(message)); 40 | } 41 | 42 | @Override 43 | public int[] getAllDimensions() { 44 | Integer[] boxed = DimensionManager.getIDs(); 45 | int[] unboxed = new int[boxed.length]; 46 | for (int i = 0; i < boxed.length; i++) { 47 | unboxed[i] = boxed[i]; 48 | } 49 | return unboxed; 50 | } 51 | 52 | @Override 53 | public double getTickTime(int dimension) { 54 | MinecraftServer server = MinecraftServer.getServer(); 55 | long[] tickTimes = server.worldTickTimes.get(dimension); 56 | long sum = 0; 57 | for (int i = 0; i < tickTimes.length; i++) { 58 | sum += tickTimes[i]; 59 | } 60 | return sum / tickTimes.length * 1.0E-6D; 61 | } 62 | 63 | @Override 64 | @SuppressWarnings("unchecked") 65 | public Set getOnlinePlayers() { 66 | return (Set)MinecraftServer.getServer().getConfigurationManager().playerEntityList.stream() 67 | .map(it -> ((EntityPlayer)it).getDisplayName()) 68 | .collect(Collectors.toSet()); 69 | } 70 | 71 | @Override 72 | public void executeCommand(String command) { 73 | MinecraftServer.getServer().getCommandManager().executeCommand(DummySender.INSTANCE, command); 74 | } 75 | 76 | 77 | 78 | @Override 79 | public String teleportPlayerToSpawn(String username) { 80 | MinecraftServer server = MinecraftServer.getServer(); 81 | WorldServer world = server.worldServerForDimension(0); 82 | ChunkCoordinates spawn = world.getSpawnPoint(); 83 | ServerConfigurationManager configManager = server.getConfigurationManager(); 84 | EntityPlayerMP player = configManager.getPlayerByUsername(username); 85 | if (player != null) { 86 | if (player.dimension != 0) { 87 | int original = player.dimension; 88 | configManager.transferPlayerToDimension(player, 0, new DummyTeleporter(world)); 89 | 90 | if (original == 1 && player.isEntityAlive()) { 91 | world.spawnEntityInWorld(player); 92 | world.updateEntityWithOptionalForce(player, false); 93 | } 94 | } 95 | player.setPositionAndUpdate(spawn.posX + 0.5d, spawn.posY, spawn.posZ + 0.5d); 96 | } else { 97 | GameProfile profile = server.getPlayerProfileCache().getGameProfileForUsername(username); 98 | 99 | if (profile != null && profile.isComplete()) { 100 | FakePlayer fakePlayer = FakePlayerFactory.get(world, profile); 101 | IPlayerFileData saveHandler = world.getSaveHandler().getPlayerNBTManager(); 102 | NBTTagCompound tag = saveHandler.readPlayerData(fakePlayer); 103 | 104 | if (tag == null) { 105 | return "Unknown player: " + username; 106 | } 107 | 108 | fakePlayer.dimension = 0; 109 | fakePlayer.posX = spawn.posX + 0.5d; 110 | fakePlayer.posY = spawn.posY; 111 | fakePlayer.posZ = spawn.posZ + 0.5d; 112 | 113 | saveHandler.writePlayerData(fakePlayer); 114 | } else { 115 | return "Unknown player: " + username; 116 | } 117 | } 118 | return null; 119 | } 120 | 121 | @Override 122 | public long getWorldTime(int dimension) { 123 | return MinecraftServer.getServer().worldServerForDimension(dimension).getWorldTime(); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /1.7.10/src/main/java/net/shadowfacts/discordchat/one_seven_ten/OneSevenTenMod.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_seven_ten; 2 | 3 | import cpw.mods.fml.common.FMLCommonHandler; 4 | import cpw.mods.fml.common.Mod; 5 | import cpw.mods.fml.common.event.FMLPreInitializationEvent; 6 | import cpw.mods.fml.common.event.FMLServerStartedEvent; 7 | import cpw.mods.fml.common.event.FMLServerStartingEvent; 8 | import cpw.mods.fml.common.event.FMLServerStoppedEvent; 9 | import net.minecraftforge.common.MinecraftForge; 10 | import net.shadowfacts.discordchat.api.IConfig; 11 | import net.shadowfacts.discordchat.api.IDiscordChat; 12 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 13 | import net.shadowfacts.discordchat.core.DiscordChat; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | 18 | /** 19 | * @author shadowfacts 20 | */ 21 | @Mod(modid = OneSevenTenMod.MOD_ID, name = OneSevenTenMod.NAME, version = OneSevenTenMod.VERSION, acceptableRemoteVersions = "*") 22 | public class OneSevenTenMod { 23 | 24 | public static final String MOD_ID = "discordchat"; 25 | public static final String NAME = "Discord Chat"; 26 | public static final String VERSION = "@VERSION@"; 27 | 28 | public static IMinecraftAdapter minecraftAdapter = new OneSevenTenAdapter(); 29 | public static IConfig config; 30 | public static IDiscordChat discordChat; 31 | 32 | @Mod.EventHandler 33 | public void preInit(FMLPreInitializationEvent event) throws IOException { 34 | discordChat = new DiscordChat(new File(event.getModConfigurationDirectory(), "shadowfacts/DiscordChat/"), minecraftAdapter); 35 | config = discordChat.getConfig(); 36 | discordChat.connect(); 37 | 38 | MinecraftForge.EVENT_BUS.register(new ForgeEventHandler()); 39 | FMLCommonHandler.instance().bus().register(new FMLEventHandler()); 40 | } 41 | 42 | @Mod.EventHandler 43 | public void serverStarting(FMLServerStartingEvent event) { 44 | event.registerServerCommand(new CommandDC(discordChat)); 45 | } 46 | 47 | @Mod.EventHandler 48 | public void serverStarted(FMLServerStartedEvent event) { 49 | discordChat.start(); 50 | if (config.sendServerOnlineOfflineMessages()) discordChat.sendMessage("Server is online"); 51 | } 52 | 53 | @Mod.EventHandler 54 | public void serverStopped(FMLServerStoppedEvent event) { 55 | if (config.sendServerOnlineOfflineMessages()) discordChat.sendMessage("Server is offline"); 56 | discordChat.stop(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /1.7.10/src/main/resources/assets/discordchat/default.conf: -------------------------------------------------------------------------------- 1 | discordchat { 2 | discord { 3 | token = "" 4 | server = "" 5 | channels = [] 6 | } 7 | 8 | commands { 9 | prefix = "!" 10 | 11 | permission { 12 | commands = "global" 13 | help = "global" 14 | reload = "admin" 15 | roleid = "global" 16 | exec = "admin" 17 | online = "global" 18 | tps = "global" 19 | unstick = "approved" 20 | time = "global" 21 | permission = "global" 22 | setPermission = "admin" 23 | tell = "global" 24 | } 25 | } 26 | 27 | relay { 28 | deaths = true 29 | achievements = true 30 | joinleave = true 31 | onlineoffline = true 32 | } 33 | 34 | filter { 35 | mode = "none" 36 | filter = "" 37 | stripFilter = false 38 | } 39 | 40 | format { 41 | # Default color if the role of the user has no color on Discord (eg. @everyone) 42 | defaultColor = "§f" 43 | 44 | # Format for a normal message from MC to Discord. 45 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 46 | fromMC = "MC » <$1> $2" 47 | 48 | # Format for a normal message from Discord to MC. 49 | # $1 will be replaced with the channel, $2 will be replaced with the sender's username, $3 will be replaced with the message, and $4 will be replaced with the MC color (e.g. §3) closest to the Discord user's color. 50 | fromDiscord = "#$1 » <$4$2§f> $3" 51 | 52 | # Format for a private message from MC to Discord. 53 | # $1 will be replaced with the sender's username and $2 will be replaced with the message. 54 | fromMCPrivate = "MC » <$1> $2" 55 | 56 | # Format for a private message from Discord to MC. 57 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 58 | fromDiscordPrivate = "Discord » <$1> $2" 59 | 60 | # Format for a player death message from MC to Discord. 61 | # $1 will be replaced with the player's username and $2 will be replaced with the death message 62 | death = "MC » $2" 63 | 64 | # Format for a player achievement message from MC to Discord. 65 | # $1 will be replaced with the player's username and $2 will be replaced with the achievement 66 | achievement = "MC » $1 has just earned the achievement $2" 67 | 68 | # Format for a player join message from MC to Discord. 69 | # $1 will be replaced with the player's username 70 | join = "MC » $1 joined the game" 71 | 72 | # Format for a player leave message from MC to Discord. 73 | # $1 will be replaced with the player's username 74 | leave = "MC » $1 left the game" 75 | 76 | # Format applied to every command response message. 77 | # Used for differentiating command responses from multiple bot instances. 78 | # $1 will be replaced with the message 79 | command = "$1" 80 | } 81 | } -------------------------------------------------------------------------------- /1.8.9/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | maven { 5 | name "forge" 6 | url "http://files.minecraftforge.net/maven/" 7 | } 8 | } 9 | dependencies { 10 | classpath "net.minecraftforge.gradle:ForgeGradle:2.1-SNAPSHOT" 11 | } 12 | } 13 | 14 | plugins { 15 | id "com.github.johnrengelman.shadow" version "1.2.3" 16 | } 17 | 18 | apply plugin: "net.minecraftforge.gradle.forge" 19 | 20 | sourceCompatibility = JavaVersion.VERSION_1_8 21 | targetCompatibility = JavaVersion.VERSION_1_8 22 | 23 | version = mc_version + "-" + mod_version 24 | 25 | archivesBaseName = "DiscordChat" 26 | 27 | minecraft { 28 | version = mc_version + "-" + forge_version 29 | runDir = "run" 30 | 31 | mappings = mcp_mappings 32 | 33 | replaceIn "OneEightNineMod.java" 34 | replace "@VERSION@", mod_version 35 | } 36 | 37 | processResources { 38 | inputs.property "version", project.version 39 | inputs.property "mcversion", project.minecraft.version 40 | 41 | from (sourceSets.main.resources.srcDirs) { 42 | include "mcmod.info" 43 | expand "version": project.version, "mcversion": project.minecraft.version 44 | } 45 | from (sourceSets.main.resources.srcDirs) { 46 | exclude "mcmod.info" 47 | } 48 | } 49 | 50 | repositories { 51 | maven { 52 | name "shadowfacts" 53 | url "http://maven.shadowfacts.net/" 54 | } 55 | flatDir { 56 | dirs "../core/libs" 57 | } 58 | } 59 | 60 | dependencies { 61 | compile project(":core") 62 | } 63 | 64 | shadowJar { 65 | classifier = "" 66 | relocate "com.iwebpp.crypto", "net.shadowfacts.discordchat.repack.com.iwebpp.crypto" 67 | relocate "com.neovisionaries.ws.client", "net.shadowfacts.discordchat.repack.com.neovisionaries.ws.client" 68 | relocate "com.sun.jna", "net.shadowfacts.discordchat.repack.com.sun.jna" 69 | relocate "com.vdurmont.emoji", "net.shadowfacts.discordchat.repack.com.vdurmont.emoji" 70 | relocate "gnu.trove", "net.shadowfacts.discordchat.gnu.trove" 71 | relocate "net.dv8tion.jda", "net.shadowfacts.discordchat.repack.net.dv8tion.jda" 72 | relocate "okhttp3", "net.shadowfacts.discordchat.repack.okhttp3" 73 | relocate "okio", "net.shadowfacts.discordchat.repack.okio" 74 | relocate "org.apache.commons.collections4", "net.shadowfacts.discordchat.repack.org.apache.commons.collections4" 75 | relocate "org.apache.commons.lang3", "net.shadowfacts.discordchat.repack.org.apache.commons.lang3" 76 | relocate "org.json", "net.shadowfacts.discordchat.repack.org.json" 77 | relocate "tomp2p.opuswrapper", "net.shadowfacts.discordchat.repack.tomp2p.opuswrapper" 78 | 79 | dependencies { 80 | include(project(":core")) 81 | include(dependency("JDA:JDA:3.3.0_260:withDependencies")) 82 | include(dependency("com.vdurmont:emoji-java:3.1.3")) 83 | include(dependency("net.shadowfacts:ShadowLib:1.9.0")) 84 | } 85 | } 86 | 87 | reobf { shadowJar { mappingType = "SEARGE" } } 88 | tasks.reobfShadowJar.mustRunAfter shadowJar 89 | -------------------------------------------------------------------------------- /1.8.9/gradle.properties: -------------------------------------------------------------------------------- 1 | group = net.shadowfacts 2 | mc_version = 1.8.9 3 | mcp_mappings = stable_22 4 | forge_version = 11.15.1.1902-1.8.9 -------------------------------------------------------------------------------- /1.8.9/src/main/java/net/shadowfacts/discordchat/one_eight_nine/CommandDC.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eight_nine; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.dv8tion.jda.core.entities.Role; 5 | import net.minecraft.command.CommandBase; 6 | import net.minecraft.command.CommandException; 7 | import net.minecraft.command.ICommandSender; 8 | import net.minecraft.command.WrongUsageException; 9 | import net.minecraft.server.MinecraftServer; 10 | import net.minecraft.util.ChatComponentText; 11 | import net.minecraft.util.ChatComponentTranslation; 12 | import net.shadowfacts.discordchat.api.IConfig; 13 | import net.shadowfacts.discordchat.api.IDiscordChat; 14 | import net.shadowfacts.discordchat.api.ILogger; 15 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 16 | import net.shadowfacts.discordchat.api.permission.Permission; 17 | 18 | import java.io.IOException; 19 | import java.util.Arrays; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.function.BiConsumer; 24 | 25 | /** 26 | * @author shadowfacts 27 | */ 28 | public class CommandDC extends CommandBase { 29 | 30 | private IDiscordChat discordChat; 31 | private ILogger logger; 32 | private IConfig config; 33 | private IPermissionManager permissionManager; 34 | 35 | private Map> subcommands = new HashMap<>(); 36 | 37 | public CommandDC(IDiscordChat discordChat) { 38 | this.discordChat = discordChat; 39 | logger = discordChat.getLogger(); 40 | config = discordChat.getConfig(); 41 | permissionManager = discordChat.getPermissionManager(); 42 | 43 | subcommands.put("setpermission", this::setPermission); 44 | subcommands.put("tell", this::tell); 45 | } 46 | 47 | @Override 48 | public String getCommandName() { 49 | return "discordchat"; 50 | } 51 | 52 | @Override 53 | public String getCommandUsage(ICommandSender sender) { 54 | return "/discordchat "; 55 | } 56 | 57 | @Override 58 | public List getCommandAliases() { 59 | return ImmutableList.of("dc"); 60 | } 61 | 62 | @Override 63 | public void processCommand(ICommandSender sender, String[] args) throws CommandException { 64 | if (args.length < 1) { 65 | throw new WrongUsageException(getCommandUsage(sender)); 66 | } 67 | 68 | String command = args[0].toLowerCase(); 69 | args = Arrays.copyOfRange(args, 1, args.length); 70 | 71 | if (!subcommands.containsKey(command)) { 72 | throw new WrongUsageException(getCommandUsage(sender)); 73 | } 74 | 75 | subcommands.get(command).accept(sender, args); 76 | } 77 | 78 | private void setPermission(ICommandSender sender, String[] args) { 79 | if (args.length < 2) { 80 | wrongUsage(sender, "/discordchat setPermission "); 81 | } 82 | 83 | Permission permission = Permission.valueOf(args[args.length - 1].toUpperCase()); 84 | 85 | String role = String.join(" ", Arrays.copyOfRange(args, 0, args.length - 1)); 86 | List roles = discordChat.getJDA().getGuildById(config.getServerID()).getRolesByName(role, true); 87 | if (roles.isEmpty()) { 88 | sender.addChatMessage(new ChatComponentText("No such role: " + role)); 89 | return; 90 | } 91 | 92 | permissionManager.set(roles.get(0), permission); 93 | 94 | try { 95 | permissionManager.save(); 96 | sender.addChatMessage(new ChatComponentText("Permissions updated")); 97 | } catch (IOException e) { 98 | logger.error(e, "Unable to save permissions"); 99 | } 100 | } 101 | 102 | private void tell(ICommandSender sender, String[] args) { 103 | if (args.length < 2) { 104 | wrongUsage(sender, "/discordchat tell "); 105 | } else { 106 | String user = args[0]; 107 | String message = ""; 108 | for (int i = 1; i < args.length; i++) { 109 | message += args[i]; 110 | if (i != args.length - 1) message += " "; 111 | } 112 | discordChat.sendPrivateMessage(sender.getName(), message, user); 113 | } 114 | } 115 | 116 | private void wrongUsage(ICommandSender sender, String usage) { 117 | sender.addChatMessage(new ChatComponentTranslation("commands.generic.usage", usage)); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /1.8.9/src/main/java/net/shadowfacts/discordchat/one_eight_nine/DummySender.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eight_nine; 2 | 3 | import net.minecraft.command.CommandResultStats; 4 | import net.minecraft.command.ICommandSender; 5 | import net.minecraft.entity.Entity; 6 | import net.minecraft.server.MinecraftServer; 7 | import net.minecraft.util.BlockPos; 8 | import net.minecraft.util.ChatComponentText; 9 | import net.minecraft.util.IChatComponent; 10 | import net.minecraft.util.Vec3; 11 | import net.minecraft.world.World; 12 | 13 | /** 14 | * @author shadowfacts 15 | */ 16 | public class DummySender implements ICommandSender { 17 | 18 | public static final ICommandSender INSTANCE = new DummySender(); 19 | 20 | @Override 21 | public String getName() { 22 | return "[DiscordChat]"; 23 | } 24 | 25 | @Override 26 | public IChatComponent getDisplayName() { 27 | return new ChatComponentText(getName()); 28 | } 29 | 30 | @Override 31 | public void addChatMessage(IChatComponent component) { 32 | OneEightNineMod.discordChat.sendMessage(OneEightNineMod.discordChat.getFormatter().command("```" + component.getUnformattedText() + "```")); 33 | } 34 | 35 | @Override 36 | public boolean canCommandSenderUseCommand(int permLevel, String commandName) { 37 | return true; 38 | } 39 | 40 | @Override 41 | public BlockPos getPosition() { 42 | return BlockPos.ORIGIN; 43 | } 44 | 45 | @Override 46 | public Vec3 getPositionVector() { 47 | return new Vec3(0, 0, 0); 48 | } 49 | 50 | @Override 51 | public World getEntityWorld() { 52 | return MinecraftServer.getServer().worldServerForDimension(0); 53 | } 54 | 55 | @Override 56 | public Entity getCommandSenderEntity() { 57 | return null; 58 | } 59 | 60 | @Override 61 | public boolean sendCommandFeedback() { 62 | return false; 63 | } 64 | 65 | @Override 66 | public void setCommandStat(CommandResultStats.Type type, int amount) { 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /1.8.9/src/main/java/net/shadowfacts/discordchat/one_eight_nine/DummyTeleporter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eight_nine; 2 | 3 | import net.minecraft.entity.Entity; 4 | import net.minecraft.world.Teleporter; 5 | import net.minecraft.world.WorldServer; 6 | 7 | /** 8 | * @author shadowfacts 9 | */ 10 | public class DummyTeleporter extends Teleporter { 11 | 12 | public DummyTeleporter(WorldServer world) { 13 | super(world); 14 | } 15 | 16 | @Override 17 | public void placeInPortal(Entity entity, float rotationYaw) { 18 | 19 | } 20 | 21 | @Override 22 | public boolean placeInExistingPortal(Entity entity, float rotationYaw) { 23 | entity.motionX = 0; 24 | entity.motionY = 0; 25 | entity.motionZ = 0; 26 | entity.fallDistance = 0; 27 | return true; 28 | } 29 | 30 | @Override 31 | public boolean makePortal(Entity entity) { 32 | return true; 33 | } 34 | 35 | @Override 36 | public void removeStalePortalLocations(long worldTime) { 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /1.8.9/src/main/java/net/shadowfacts/discordchat/one_eight_nine/ForgeEventHandler.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eight_nine; 2 | 3 | import net.minecraft.entity.player.EntityPlayer; 4 | import net.minecraft.entity.player.EntityPlayerMP; 5 | import net.minecraft.util.ChatComponentText; 6 | import net.minecraft.util.IChatComponent; 7 | import net.minecraftforge.common.util.FakePlayer; 8 | import net.minecraftforge.event.ServerChatEvent; 9 | import net.minecraftforge.event.entity.living.LivingDeathEvent; 10 | import net.minecraftforge.event.entity.player.AchievementEvent; 11 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 12 | import net.minecraftforge.fml.common.gameevent.PlayerEvent; 13 | 14 | /** 15 | * @author shadowfacts 16 | */ 17 | public class ForgeEventHandler { 18 | 19 | @SubscribeEvent 20 | public void onServerChat(ServerChatEvent event) { 21 | String message = OneEightNineMod.discordChat.filterMCMessage(event.message); 22 | if (message != null) { 23 | OneEightNineMod.discordChat.sendMessage(OneEightNineMod.discordChat.getFormatter().fromMC(event.player.getName(), message)); 24 | } 25 | } 26 | 27 | @SubscribeEvent 28 | public void onLivingDeath(LivingDeathEvent event) { 29 | if (OneEightNineMod.config.sendDeathMessages() && event.entityLiving instanceof EntityPlayer && !(event.entityLiving instanceof FakePlayer)) { 30 | EntityPlayer player = (EntityPlayer)event.entityLiving; 31 | OneEightNineMod.discordChat.sendMessage(OneEightNineMod.discordChat.getFormatter().death(player.getName(), player.getCombatTracker().getDeathMessage().getUnformattedText())); 32 | } 33 | } 34 | 35 | @SubscribeEvent 36 | public void onPlayerReceiveAchievement(AchievementEvent event) { 37 | if (OneEightNineMod.config.sendAchievementMessages() && event.entityPlayer instanceof EntityPlayerMP) { 38 | if (((EntityPlayerMP) event.entityPlayer).getStatFile().hasAchievementUnlocked(event.achievement)) { 39 | return; 40 | } 41 | if (!((EntityPlayerMP) event.entityPlayer).getStatFile().canUnlockAchievement(event.achievement)) { 42 | return; 43 | } 44 | IChatComponent achievementComponent = event.achievement.getStatName(); 45 | IChatComponent achievementText = new ChatComponentText("[").appendSibling(achievementComponent).appendText("]"); 46 | OneEightNineMod.discordChat.sendMessage(OneEightNineMod.discordChat.getFormatter().achievement(event.entityPlayer.getName(), achievementText.getUnformattedText())); 47 | } 48 | } 49 | 50 | @SubscribeEvent 51 | public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { 52 | if (OneEightNineMod.config.sendJoinLeaveMessages()) { 53 | OneEightNineMod.discordChat.sendMessage(OneEightNineMod.discordChat.getFormatter().join(event.player.getName())); 54 | } 55 | } 56 | 57 | @SubscribeEvent 58 | public void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { 59 | if (OneEightNineMod.config.sendJoinLeaveMessages()) { 60 | OneEightNineMod.discordChat.sendMessage(OneEightNineMod.discordChat.getFormatter().leave(event.player.getName())); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /1.8.9/src/main/java/net/shadowfacts/discordchat/one_eight_nine/OneEightNineAdapter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eight_nine; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import net.minecraft.entity.player.EntityPlayer; 5 | import net.minecraft.entity.player.EntityPlayerMP; 6 | import net.minecraft.nbt.NBTTagCompound; 7 | import net.minecraft.server.MinecraftServer; 8 | import net.minecraft.server.management.ServerConfigurationManager; 9 | import net.minecraft.util.BlockPos; 10 | import net.minecraft.util.ChatComponentText; 11 | import net.minecraft.world.WorldServer; 12 | import net.minecraft.world.storage.IPlayerFileData; 13 | import net.minecraftforge.common.DimensionManager; 14 | import net.minecraftforge.common.ForgeHooks; 15 | import net.minecraftforge.common.util.FakePlayer; 16 | import net.minecraftforge.common.util.FakePlayerFactory; 17 | import net.minecraftforge.fml.common.FMLCommonHandler; 18 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 19 | 20 | import java.util.Set; 21 | import java.util.stream.Collectors; 22 | 23 | /** 24 | * @author shadowfacts 25 | */ 26 | public class OneEightNineAdapter implements IMinecraftAdapter { 27 | 28 | @Override 29 | public void sendMessage(String message) { 30 | MinecraftServer server = MinecraftServer.getServer(); 31 | if (server != null) { 32 | server.getConfigurationManager().sendChatMsg(ForgeHooks.newChatWithLinks(message)); 33 | } 34 | } 35 | 36 | @Override 37 | public void sendMessageToPlayer(String message, String player) { 38 | MinecraftServer server = MinecraftServer.getServer(); 39 | EntityPlayer mcPlayer = server.getConfigurationManager().getPlayerByUsername(player); 40 | mcPlayer.addChatMessage(ForgeHooks.newChatWithLinks(message)); 41 | } 42 | 43 | @Override 44 | public int[] getAllDimensions() { 45 | Integer[] boxed = DimensionManager.getIDs(); 46 | int[] unboxed = new int[boxed.length]; 47 | for (int i = 0; i < boxed.length; i++) { 48 | unboxed[i] = boxed[i]; 49 | } 50 | return unboxed; 51 | } 52 | 53 | @Override 54 | public double getTickTime(int dimension) { 55 | MinecraftServer server = MinecraftServer.getServer(); 56 | long[] tickTimes = server.worldTickTimes.get(dimension); 57 | long sum = 0; 58 | for (int i = 0; i < tickTimes.length; i++) { 59 | sum += tickTimes[i]; 60 | } 61 | return sum / tickTimes.length * 1.0E-6D; 62 | } 63 | 64 | @Override 65 | public Set getOnlinePlayers() { 66 | return MinecraftServer.getServer().getConfigurationManager().getPlayerList().stream() 67 | .map(EntityPlayer::getName) 68 | .collect(Collectors.toSet()); 69 | } 70 | 71 | @Override 72 | public void executeCommand(String command) { 73 | MinecraftServer.getServer().callFromMainThread(() -> { 74 | MinecraftServer.getServer().getCommandManager().executeCommand(DummySender.INSTANCE, command); 75 | return null; 76 | }); 77 | } 78 | 79 | @Override 80 | public String teleportPlayerToSpawn(String username) { 81 | MinecraftServer server = MinecraftServer.getServer(); 82 | WorldServer world = server.worldServerForDimension(0); 83 | BlockPos spawn = world.getSpawnPoint(); 84 | ServerConfigurationManager configManager = server.getConfigurationManager(); 85 | EntityPlayerMP player = configManager.getPlayerByUsername(username); 86 | if (player != null) { 87 | if (player.dimension != 0) { 88 | int original = player.dimension; 89 | configManager.transferPlayerToDimension(player, 0, new DummyTeleporter(world)); 90 | 91 | if (original == 1 && player.isEntityAlive()) { 92 | world.spawnEntityInWorld(player); 93 | world.updateEntityWithOptionalForce(player, false); 94 | } 95 | } 96 | player.setPositionAndUpdate(spawn.getX() + 0.5d, spawn.getY(), spawn.getZ() + 0.5d); 97 | } else { 98 | GameProfile profile = server.getPlayerProfileCache().getGameProfileForUsername(username); 99 | 100 | if (profile != null && profile.isComplete()) { 101 | FakePlayer fakePlayer = FakePlayerFactory.get(world, profile); 102 | IPlayerFileData saveHandler = world.getSaveHandler().getPlayerNBTManager(); 103 | NBTTagCompound tag = saveHandler.readPlayerData(fakePlayer); 104 | 105 | if (tag == null) { 106 | return "Unknown player: " + username; 107 | } 108 | 109 | fakePlayer.dimension = 0; 110 | fakePlayer.posX = spawn.getX() + 0.5d; 111 | fakePlayer.posY = spawn.getY(); 112 | fakePlayer.posZ = spawn.getZ() + 0.5d; 113 | 114 | saveHandler.writePlayerData(fakePlayer); 115 | } else { 116 | return "Unknown player: " + username; 117 | } 118 | } 119 | return null; 120 | } 121 | 122 | @Override 123 | public long getWorldTime(int dimension) { 124 | return MinecraftServer.getServer().worldServerForDimension(dimension).getWorldTime(); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /1.8.9/src/main/java/net/shadowfacts/discordchat/one_eight_nine/OneEightNineMod.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.one_eight_nine; 2 | 3 | import net.minecraftforge.common.MinecraftForge; 4 | import net.minecraftforge.fml.common.Mod; 5 | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; 6 | import net.minecraftforge.fml.common.event.FMLServerStartedEvent; 7 | import net.minecraftforge.fml.common.event.FMLServerStartingEvent; 8 | import net.minecraftforge.fml.common.event.FMLServerStoppedEvent; 9 | import net.shadowfacts.discordchat.api.IConfig; 10 | import net.shadowfacts.discordchat.api.IDiscordChat; 11 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 12 | import net.shadowfacts.discordchat.core.DiscordChat; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | 17 | /** 18 | * @author shadowfacts 19 | */ 20 | @Mod(modid = OneEightNineMod.MOD_ID, name = OneEightNineMod.NAME, version = OneEightNineMod.VERSION, acceptableRemoteVersions = "*") 21 | public class OneEightNineMod { 22 | 23 | public static final String MOD_ID = "discordchat"; 24 | public static final String NAME = "Discord Chat"; 25 | public static final String VERSION = "@VERSION@"; 26 | 27 | public static IMinecraftAdapter minecraftAdapter = new OneEightNineAdapter(); 28 | public static IConfig config; 29 | public static IDiscordChat discordChat; 30 | 31 | @Mod.EventHandler 32 | public void preInit(FMLPreInitializationEvent event) throws IOException { 33 | discordChat = new DiscordChat(new File(event.getModConfigurationDirectory(), "shadowfacts/DiscordChat/"), minecraftAdapter); 34 | config = discordChat.getConfig(); 35 | discordChat.connect(); 36 | 37 | MinecraftForge.EVENT_BUS.register(new ForgeEventHandler()); 38 | } 39 | 40 | @Mod.EventHandler 41 | public void serverStarting(FMLServerStartingEvent event) { 42 | event.registerServerCommand(new CommandDC(discordChat)); 43 | } 44 | 45 | @Mod.EventHandler 46 | public void serverStarted(FMLServerStartedEvent event) { 47 | discordChat.start(); 48 | if (config.sendServerOnlineOfflineMessages()) discordChat.sendMessage("Server is online"); 49 | } 50 | 51 | @Mod.EventHandler 52 | public void serverStopped(FMLServerStoppedEvent event) { 53 | if (config.sendServerOnlineOfflineMessages()) discordChat.sendMessage("Server is offline"); 54 | discordChat.stop(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /1.8.9/src/main/resources/assets/discordchat/default.conf: -------------------------------------------------------------------------------- 1 | discordchat { 2 | discord { 3 | token = "" 4 | server = "" 5 | channels = [] 6 | } 7 | 8 | commands { 9 | prefix = "!" 10 | 11 | permission { 12 | commands = "global" 13 | help = "global" 14 | reload = "admin" 15 | roleid = "global" 16 | exec = "admin" 17 | online = "global" 18 | tps = "global" 19 | unstick = "approved" 20 | time = "global" 21 | permission = "global" 22 | setPermission = "admin" 23 | tell = "global" 24 | } 25 | } 26 | 27 | relay { 28 | deaths = true 29 | achievements = true 30 | joinleave = true 31 | onlineoffline = true 32 | } 33 | 34 | filter { 35 | mode = "none" 36 | filter = "" 37 | stripFilter = false 38 | } 39 | 40 | format { 41 | # Default color if the role of the user has no color on Discord (eg. @everyone) 42 | defaultColor = "§f" 43 | 44 | # Format for a normal message from MC to Discord. 45 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 46 | fromMC = "MC » <$1> $2" 47 | 48 | # Format for a normal message from Discord to MC. 49 | # $1 will be replaced with the channel, $2 will be replaced with the sender's username, $3 will be replaced with the message, and $4 will be replaced with the MC color (e.g. §3) closest to the Discord user's color. 50 | fromDiscord = "#$1 » <$4$2§f> $3" 51 | 52 | # Format for a private message from MC to Discord. 53 | # $1 will be replaced with the sender's username and $2 will be replaced with the message. 54 | fromMCPrivate = "MC » <$1> $2" 55 | 56 | # Format for a private message from Discord to MC. 57 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 58 | fromDiscordPrivate = "Discord » <$1> $2" 59 | 60 | # Format for a player death message from MC to Discord. 61 | # $1 will be replaced with the player's username and $2 will be replaced with the death message 62 | death = "MC » $2" 63 | 64 | # Format for a player achievement message from MC to Discord. 65 | # $1 will be replaced with the player's username and $2 will be replaced with the achievement 66 | achievement = "MC » $1 has just earned the achievement $2" 67 | 68 | # Format for a player join message from MC to Discord. 69 | # $1 will be replaced with the player's username 70 | join = "MC » $1 joined the game" 71 | 72 | # Format for a player leave message from MC to Discord. 73 | # $1 will be replaced with the player's username 74 | leave = "MC » $1 left the game" 75 | 76 | # Format applied to every command response message. 77 | # Used for differentiating command responses from multiple bot instances. 78 | # $1 will be replaced with the message 79 | command = "$1" 80 | } 81 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. GNU LESSER GENERAL PUBLIC LICENSE 166 | Version 3, 29 June 2007 167 | 168 | Copyright (C) 2007 Free Software Foundation, Inc. 169 | Everyone is permitted to copy and distribute verbatim copies 170 | of this license document, but changing it is not allowed. 171 | 172 | 173 | This version of the GNU Lesser General Public License incorporates 174 | the terms and conditions of version 3 of the GNU General Public 175 | License, supplemented by the additional permissions listed below. 176 | 177 | 0. Additional Definitions. 178 | 179 | As used herein, "this License" refers to version 3 of the GNU Lesser 180 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 181 | General Public License. 182 | 183 | "The Library" refers to a covered work governed by this License, 184 | other than an Application or a Combined Work as defined below. 185 | 186 | An "Application" is any work that makes use of an interface provided 187 | by the Library, but which is not otherwise based on the Library. 188 | Defining a subclass of a class defined by the Library is deemed a mode 189 | of using an interface provided by the Library. 190 | 191 | A "Combined Work" is a work produced by combining or linking an 192 | Application with the Library. The particular version of the Library 193 | with which the Combined Work was made is also called the "Linked 194 | Version". 195 | 196 | The "Minimal Corresponding Source" for a Combined Work means the 197 | Corresponding Source for the Combined Work, excluding any source code 198 | for portions of the Combined Work that, considered in isolation, are 199 | based on the Application, and not on the Linked Version. 200 | 201 | The "Corresponding Application Code" for a Combined Work means the 202 | object code and/or source code for the Application, including any data 203 | and utility programs needed for reproducing the Combined Work from the 204 | Application, but excluding the System Libraries of the Combined Work. 205 | 206 | 1. Exception to Section 3 of the GNU GPL. 207 | 208 | You may convey a covered work under sections 3 and 4 of this License 209 | without being bound by section 3 of the GNU GPL. 210 | 211 | 2. Conveying Modified Versions. 212 | 213 | If you modify a copy of the Library, and, in your modifications, a 214 | facility refers to a function or data to be supplied by an Application 215 | that uses the facility (other than as an argument passed when the 216 | facility is invoked), then you may convey a copy of the modified 217 | version: 218 | 219 | a) under this License, provided that you make a good faith effort to 220 | ensure that, in the event an Application does not supply the 221 | function or data, the facility still operates, and performs 222 | whatever part of its purpose remains meaningful, or 223 | 224 | b) under the GNU GPL, with none of the additional permissions of 225 | this License applicable to that copy. 226 | 227 | 3. Object Code Incorporating Material from Library Header Files. 228 | 229 | The object code form of an Application may incorporate material from 230 | a header file that is part of the Library. You may convey such object 231 | code under terms of your choice, provided that, if the incorporated 232 | material is not limited to numerical parameters, data structure 233 | layouts and accessors, or small macros, inline functions and templates 234 | (ten or fewer lines in length), you do both of the following: 235 | 236 | a) Give prominent notice with each copy of the object code that the 237 | Library is used in it and that the Library and its use are 238 | covered by this License. 239 | 240 | b) Accompany the object code with a copy of the GNU GPL and this license 241 | document. 242 | 243 | 4. Combined Works. 244 | 245 | You may convey a Combined Work under terms of your choice that, 246 | taken together, effectively do not restrict modification of the 247 | portions of the Library contained in the Combined Work and reverse 248 | engineering for debugging such modifications, if you also do each of 249 | the following: 250 | 251 | a) Give prominent notice with each copy of the Combined Work that 252 | the Library is used in it and that the Library and its use are 253 | covered by this License. 254 | 255 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 256 | document. 257 | 258 | c) For a Combined Work that displays copyright notices during 259 | execution, include the copyright notice for the Library among 260 | these notices, as well as a reference directing the user to the 261 | copies of the GNU GPL and this license document. 262 | 263 | d) Do one of the following: 264 | 265 | 0) Convey the Minimal Corresponding Source under the terms of this 266 | License, and the Corresponding Application Code in a form 267 | suitable for, and under terms that permit, the user to 268 | recombine or relink the Application with a modified version of 269 | the Linked Version to produce a modified Combined Work, in the 270 | manner specified by section 6 of the GNU GPL for conveying 271 | Corresponding Source. 272 | 273 | 1) Use a suitable shared library mechanism for linking with the 274 | Library. A suitable mechanism is one that (a) uses at run time 275 | a copy of the Library already present on the user's computer 276 | system, and (b) will operate properly with a modified version 277 | of the Library that is interface-compatible with the Linked 278 | Version. 279 | 280 | e) Provide Installation Information, but only if you would otherwise 281 | be required to provide such information under section 6 of the 282 | GNU GPL, and only to the extent that such information is 283 | necessary to install and execute a modified version of the 284 | Combined Work produced by recombining or relinking the 285 | Application with a modified version of the Linked Version. (If 286 | you use option 4d0, the Installation Information must accompany 287 | the Minimal Corresponding Source and Corresponding Application 288 | Code. If you use option 4d1, you must provide the Installation 289 | Information in the manner specified by section 6 of the GNU GPL 290 | for conveying Corresponding Source.) 291 | 292 | 5. Combined Libraries. 293 | 294 | You may place library facilities that are a work based on the 295 | Library side by side in a single library together with other library 296 | facilities that are not Applications and are not covered by this 297 | License, and convey such a combined library under terms of your 298 | choice, if you do both of the following: 299 | 300 | a) Accompany the combined library with a copy of the same work based 301 | on the Library, uncombined with any other library facilities, 302 | conveyed under the terms of this License. 303 | 304 | b) Give prominent notice with the combined library that part of it 305 | is a work based on the Library, and explaining where to find the 306 | accompanying uncombined form of the same work. 307 | 308 | 6. Revised Versions of the GNU Lesser General Public License. 309 | 310 | The Free Software Foundation may publish revised and/or new versions 311 | of the GNU Lesser General Public License from time to time. Such new 312 | versions will be similar in spirit to the present version, but may 313 | differ in detail to address new problems or concerns. 314 | 315 | Each version is given a distinguishing version number. If the 316 | Library as you received it specifies that a certain numbered version 317 | of the GNU Lesser General Public License "or any later version" 318 | applies to it, you have the option of following the terms and 319 | conditions either of that published version or of any later version 320 | published by the Free Software Foundation. If the Library as you 321 | received it does not specify a version number of the GNU Lesser 322 | General Public License, you may choose any version of the GNU Lesser 323 | General Public License ever published by the Free Software Foundation. 324 | 325 | If the Library as you received it specifies that a proxy can decide 326 | whether future versions of the GNU Lesser General Public License shall 327 | apply, that proxy's public statement of acceptance of any version is 328 | permanent authorization for you to choose that version for the 329 | Library. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DiscordChat 2 | Discord chat is a simple server-side mod that connects Minecraft communication with a Discord server. 3 | 4 | ## [Setup Instructions](https://rtfm.shadowfacts.net/discord-chat/) -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // configure(subprojects.findAll { it.name != "core" }) { 2 | // version = mc_version + "-" + mod_version 3 | 4 | // dependencies { 5 | // compile project(":core") 6 | // } 7 | 8 | // shadowJar { 9 | // classifier = "" 10 | 11 | // relocate "com.mashape.unirest", "net.shadowfacts.discordchat.repack.com.mashape.unirest" 12 | // relocate "com.neovisionaries.ws.client", "net.shadowfacts.discordchat.repack.com.neovisionaries.ws.client" 13 | // relocate "com.sun.jna", "net.shadowfacts.discordchat.repack.com.sun.jna" 14 | // relocate "javax.sound", "net.shadowfacts.discordchat.repack.javax.sound" 15 | // relocate "javazoom", "net.shadowfacts.discordchat.repack.javazoom" 16 | // relocate "net.dv8tion.jda", "net.shadowfacts.discordchat.repack.net.dv8tion.jda" 17 | // relocate "net.sourceforge.jaad", "net.shadowfacts.discordchat.repack.net.sourceforge.jaad" 18 | // relocate "org.apache.commons.codec", "net.shadowfacts.discordchat.repack.org.apache.commons.codec" 19 | // relocate "org.apache.commons.lang3", "net.shadowfacts.discordchat.repack.org.apache.commons.lang3" 20 | // relocate "org.json", "net.shadowfacts.discordchat.repack.org.json" 21 | // relocate "org.kc7bfi.jflac", "net.shadowfacts.discordchat.repack.orgkc7bfi.jflac" 22 | // relocate "org.tritonus", "net.shadowfacts.discordchat.repack.org.tritonus" 23 | // relocate "tomp2p.opuswrapper", "net.shadowfacts.discordchat.repack.tomp2p.opuswrapper" 24 | 25 | // relocate "org.apache.http", "net.shadowfacts.discordchat.repack.org.apache.http" 26 | // relocate "org.apache.commons.logging", "net.shadowfacts.discordchat.repack.org.apache.commons.logging" 27 | 28 | // dependencies { 29 | // include(dependency(":core")) 30 | // } 31 | // } 32 | // } -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java" 3 | } 4 | 5 | repositories { 6 | jcenter() 7 | maven { 8 | name "shadowfacts" 9 | url "http://maven.shadowfacts.net/" 10 | } 11 | flatDir { 12 | dirs "libs" 13 | } 14 | } 15 | 16 | dependencies { 17 | compile group: "JDA", name: "JDA", version: jda_version, classifier: "withDependencies" 18 | compile group: "com.vdurmont", name: "emoji-java", version: emoji_version 19 | compile group: "org.apache.logging.log4j", name: "log4j-api", version: log4j_version 20 | compile group: "com.typesafe", name: "config", version: config_version 21 | compile group: "com.google.code.gson", name: "gson", version: gson_version 22 | compile group: "net.shadowfacts", name: "ShadowLib", version: shadowlib_version 23 | } -------------------------------------------------------------------------------- /core/gradle.properties: -------------------------------------------------------------------------------- 1 | jda_version = 3.3.0_260 2 | emoji_version = 3.1.3 3 | log4j_version = 2.0-beta9 4 | config_version = 1.3.1 5 | gson_version = 2.8.0 6 | shadowlib_version = 1.9.0 -------------------------------------------------------------------------------- /core/libs/JDA-3.3.0_260-withDependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowfacts/DiscordChat/07a22edd374ef8007421b1d0601e565c3180c2a3/core/libs/JDA-3.3.0_260-withDependencies.jar -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/api/IConfig.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.api; 2 | 3 | import net.shadowfacts.discordchat.api.permission.Permission; 4 | 5 | import java.io.IOException; 6 | import java.util.List; 7 | 8 | /** 9 | * @author shadowfacts 10 | */ 11 | public interface IConfig { 12 | 13 | void load() throws IOException; 14 | 15 | void save() throws IOException; 16 | 17 | String getToken(); 18 | 19 | Long getServerID(); 20 | 21 | List getChannelIDs(); 22 | 23 | String getCommandPrefix(); 24 | 25 | Permission getMinimumPermission(String command); 26 | 27 | FilterMode getMCMessageFilterMode(); 28 | 29 | String getMCMessageFilter(); 30 | 31 | boolean stripFilterPart(); 32 | 33 | boolean sendDeathMessages(); 34 | 35 | boolean sendAchievementMessages(); 36 | 37 | boolean sendJoinLeaveMessages(); 38 | 39 | boolean sendServerOnlineOfflineMessages(); 40 | 41 | String getDefaultColor(); 42 | 43 | String getFromMCFormat(); 44 | 45 | String getFromDiscordFormat(); 46 | 47 | String getFromMCPrivateFormat(); 48 | 49 | String getFromDiscordPrivateFormat(); 50 | 51 | String getDeathFormat(); 52 | 53 | String getAchievementFormat(); 54 | 55 | String getJoinFormat(); 56 | 57 | String getLeaveFormat(); 58 | 59 | String getCommandFormat(); 60 | 61 | enum FilterMode { 62 | NONE, 63 | PREFIX, 64 | SUFFIX 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/api/IDiscordChat.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.api; 2 | 3 | import net.dv8tion.jda.core.JDA; 4 | import net.dv8tion.jda.core.entities.Member; 5 | import net.dv8tion.jda.core.entities.MessageChannel; 6 | import net.dv8tion.jda.core.entities.PrivateChannel; 7 | import net.shadowfacts.discordchat.api.command.ICommandManager; 8 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 9 | 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | /** 14 | * @author shadowfacts 15 | */ 16 | public interface IDiscordChat { 17 | 18 | ILogger getLogger(); 19 | 20 | IConfig getConfig(); 21 | 22 | ICommandManager getCommandManager(); 23 | 24 | IPermissionManager getPermissionManager(); 25 | 26 | IMessageFormatter getFormatter(); 27 | 28 | JDA getJDA(); 29 | 30 | IMinecraftAdapter getMinecraftAdapter(); 31 | 32 | void connect(); 33 | 34 | void start(); 35 | 36 | void stop(); 37 | 38 | default boolean sendPrivateMessage(String sender, String message, String user) { 39 | List members = getJDA().getGuildById(getConfig().getServerID()).getMembersByEffectiveName(user, true); 40 | if (!members.isEmpty()) { 41 | members.get(0).getUser().openPrivateChannel().queue(channel -> { 42 | sendMessage(getFormatter().fromMCPrivate(sender, message), channel); 43 | }); 44 | return true; 45 | } else { 46 | return false; 47 | } 48 | } 49 | 50 | default void sendMessage(String message, MessageChannel channel) { 51 | sendMessage(message, Collections.singletonList(channel)); 52 | } 53 | 54 | void sendMessage(String message, List channel); 55 | 56 | void sendMessage(String message); 57 | 58 | String filterMCMessage(String message); 59 | 60 | } 61 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/api/ILogger.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.api; 2 | 3 | /** 4 | * @author shadowfacts 5 | */ 6 | public interface ILogger { 7 | 8 | void debug(String msg, Object... args); 9 | 10 | void info(String msg, Object... args); 11 | 12 | void warn(String msg, Object... args); 13 | 14 | void warn(Throwable t, String msg, Object... args); 15 | 16 | void bigWarning(String msg); 17 | 18 | void error(String msg, Object... args); 19 | 20 | void error(Throwable t, String msg, Object... args); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/api/IMessageFormatter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.api; 2 | 3 | /** 4 | * @author shadowfacts 5 | */ 6 | public interface IMessageFormatter { 7 | 8 | String fromMC(String sender, String message); 9 | 10 | String fromDiscord(String channel, String senderColor, String sender, String message); 11 | 12 | String fromMCPrivate(String sender, String message); 13 | 14 | String fromDiscordPrivate(String sender, String message); 15 | 16 | String death(String player, String message); 17 | 18 | String achievement(String player, String achievement); 19 | 20 | String join(String player); 21 | 22 | String leave(String player); 23 | 24 | String command(String message); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/api/IMinecraftAdapter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.api; 2 | 3 | import java.util.Set; 4 | 5 | /** 6 | * @author shadowfacts 7 | */ 8 | public interface IMinecraftAdapter { 9 | 10 | void sendMessage(String message); 11 | 12 | void sendMessageToPlayer(String message, String player); 13 | 14 | int[] getAllDimensions(); 15 | 16 | double getTickTime(int dimension); 17 | 18 | Set getOnlinePlayers(); 19 | 20 | void executeCommand(String command); 21 | 22 | String teleportPlayerToSpawn(String username); 23 | 24 | long getWorldTime(int dimension); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/api/command/ICommand.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.api.command; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IDiscordChat; 6 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 7 | import net.shadowfacts.discordchat.api.permission.Permission; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * @author shadowfacts 14 | */ 15 | public interface ICommand { 16 | 17 | String getName(); 18 | 19 | Permission getMinimumPermission(); 20 | 21 | void execute(String[] args, User sender, MessageChannel channel) throws CommandException; 22 | 23 | default void sendResponse(String message, MessageChannel channel, IDiscordChat discordChat) { 24 | discordChat.sendMessage(discordChat.getFormatter().command(message), channel); 25 | } 26 | 27 | String getDescription(); 28 | 29 | String getUsage(); 30 | 31 | default List handleHelp(User sender, MessageChannel channel) { 32 | List list = new ArrayList<>(); 33 | list.add(getName() + ": " + getDescription()); 34 | list.add("Usage: " + getName() + " " + getUsage()); 35 | return list; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/api/command/ICommandManager.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.api.command; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IDiscordChat; 6 | 7 | import java.util.Collection; 8 | 9 | /** 10 | * @author shadowfacts 11 | */ 12 | public interface ICommandManager { 13 | 14 | IDiscordChat getDiscordChat(); 15 | 16 | void register(ICommand command); 17 | 18 | boolean exists(String name); 19 | 20 | ICommand get(String name); 21 | 22 | void execute(String message, User sender, MessageChannel channel); 23 | 24 | Collection getCommands(); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/api/command/exception/CommandException.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.api.command.exception; 2 | 3 | /** 4 | * @author shadowfacts 5 | */ 6 | public class CommandException extends Exception { 7 | 8 | public CommandException() { 9 | super(); 10 | } 11 | 12 | public CommandException(String message) { 13 | super(message); 14 | } 15 | 16 | public CommandException(String message, Throwable cause) { 17 | super(message, cause); 18 | } 19 | 20 | public CommandException(Throwable cause) { 21 | super(cause); 22 | } 23 | 24 | protected CommandException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 25 | super(message, cause, enableSuppression, writableStackTrace); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/api/permission/IPermissionManager.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.api.permission; 2 | 3 | import net.dv8tion.jda.core.entities.Guild; 4 | import net.dv8tion.jda.core.entities.Role; 5 | import net.dv8tion.jda.core.entities.User; 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | 10 | /** 11 | * @author shadowfacts 12 | */ 13 | public interface IPermissionManager { 14 | 15 | Permission get(Role role); 16 | 17 | default Permission get(User user, Guild guild) { 18 | Permission max = Permission.GLOBAL; 19 | List roles = guild.getMember(user).getRoles(); 20 | for (Role role : roles) { 21 | Permission permisison = get(role); 22 | if (permisison.ordinal() > max.ordinal()) { 23 | max = permisison; 24 | } 25 | } 26 | return max; 27 | } 28 | 29 | void set(Role role, Permission permission); 30 | 31 | default boolean has(Role role, Permission permission) { 32 | return get(role).has(permission); 33 | } 34 | 35 | default boolean has(User user, Guild guild, Permission permission) { 36 | return get(user, guild).has(permission); 37 | } 38 | 39 | void load() throws IOException; 40 | 41 | void save() throws IOException; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/api/permission/Permission.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.api.permission; 2 | 3 | /** 4 | * @author shadowfacts 5 | */ 6 | public enum Permission { 7 | 8 | GLOBAL, 9 | APPROVED, 10 | ADMIN; 11 | 12 | public boolean has(Permission other) { 13 | return ordinal() >= other.ordinal(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/Config.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core; 2 | 3 | import com.typesafe.config.*; 4 | import net.shadowfacts.discordchat.api.IConfig; 5 | import net.shadowfacts.discordchat.api.permission.Permission; 6 | import net.shadowfacts.shadowlib.util.IOUtils; 7 | 8 | import java.io.*; 9 | import java.nio.charset.Charset; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | /** 15 | * @author shadowfacts 16 | */ 17 | public class Config implements IConfig { 18 | 19 | private File file; 20 | private com.typesafe.config.Config config; 21 | 22 | public Config(File file) throws IOException { 23 | this.file = file; 24 | load(); 25 | } 26 | 27 | @Override 28 | public void load() throws IOException { 29 | config = ConfigFactory.load().withFallback(ConfigFactory.parseFile(file)).withFallback(ConfigFactory.load("assets/discordchat/default.conf")); 30 | 31 | // Migration handler 32 | if (config.hasPath("discordchat.discord.channel")) { 33 | String channel = config.getString("discordchat.discord.channel"); 34 | config = config.withoutPath("discordchat.discord.channel"); 35 | config = config.withValue("discordchat.discord.channels", ConfigValueFactory.fromAnyRef(Collections.singletonList(channel))); 36 | } 37 | 38 | save(); 39 | } 40 | 41 | @Override 42 | public void save() throws IOException { 43 | if (!file.exists()) { 44 | if (!file.getParentFile().exists()) { 45 | file.getParentFile().mkdirs(); 46 | } 47 | file.createNewFile(); 48 | } 49 | 50 | ConfigObject toRender = config.root().withOnlyKey("discordchat"); 51 | String s = toRender.render(ConfigRenderOptions.defaults().setOriginComments(false).setJson(false)); 52 | InputStream in = new ByteArrayInputStream(s.getBytes(Charset.forName("UTF-8"))); 53 | OutputStream out = new FileOutputStream(file); 54 | IOUtils.copy(in, out); 55 | in.close(); 56 | out.close(); 57 | 58 | } 59 | 60 | @Override 61 | public String getToken() { 62 | return config.getString("discordchat.discord.token"); 63 | } 64 | 65 | @Override 66 | public Long getServerID() { 67 | return config.getLong("discordchat.discord.server"); 68 | } 69 | 70 | @Override 71 | public List getChannelIDs() { 72 | try { 73 | return config.getLongList("discordchat.discord.channels"); 74 | } catch (ConfigException.WrongType e) { 75 | throw new RuntimeException("Unable to get channel IDs, update the config to use numeric channel IDs.", e); 76 | } 77 | } 78 | 79 | @Override 80 | public String getCommandPrefix() { 81 | return config.getString("discordchat.commands.prefix"); 82 | } 83 | 84 | @Override 85 | public Permission getMinimumPermission(String command) { 86 | return Permission.valueOf(config.getString("discordchat.commands.permission." + command).toUpperCase()); 87 | } 88 | 89 | @Override 90 | public FilterMode getMCMessageFilterMode() { 91 | return FilterMode.valueOf(config.getString("discordchat.filter.mode").toUpperCase()); 92 | } 93 | 94 | @Override 95 | public String getMCMessageFilter() { 96 | return config.getString("discordchat.filter.filter"); 97 | } 98 | 99 | @Override 100 | public boolean stripFilterPart() { 101 | return config.getBoolean("discordchat.filter.stripFilter"); 102 | } 103 | 104 | @Override 105 | public boolean sendDeathMessages() { 106 | return config.getBoolean("discordchat.relay.deaths"); 107 | } 108 | 109 | @Override 110 | public boolean sendAchievementMessages() { 111 | return config.getBoolean("discordchat.relay.achievements"); 112 | } 113 | 114 | @Override 115 | public boolean sendJoinLeaveMessages() { 116 | return config.getBoolean("discordchat.relay.joinleave"); 117 | } 118 | 119 | @Override 120 | public boolean sendServerOnlineOfflineMessages() { 121 | return config.getBoolean("discordchat.relay.onlineoffline"); 122 | } 123 | 124 | @Override 125 | public String getDefaultColor() { 126 | return config.getString("discordchat.format.defaultColor"); 127 | } 128 | 129 | @Override 130 | public String getFromMCFormat() { 131 | return config.getString("discordchat.format.fromMC"); 132 | } 133 | 134 | @Override 135 | public String getFromDiscordFormat() { 136 | return config.getString("discordchat.format.fromDiscord"); 137 | } 138 | 139 | @Override 140 | public String getFromMCPrivateFormat() { 141 | return config.getString("discordchat.format.fromMCPrivate"); 142 | } 143 | 144 | @Override 145 | public String getFromDiscordPrivateFormat() { 146 | return config.getString("discordchat.format.fromDiscordPrivate"); 147 | } 148 | 149 | @Override 150 | public String getDeathFormat() { 151 | return config.getString("discordchat.format.death"); 152 | } 153 | 154 | @Override 155 | public String getAchievementFormat() { 156 | return config.getString("discordchat.format.achievement"); 157 | } 158 | 159 | @Override 160 | public String getJoinFormat() { 161 | return config.getString("discordchat.format.join"); 162 | } 163 | 164 | @Override 165 | public String getLeaveFormat() { 166 | return config.getString("discordchat.format.leave"); 167 | } 168 | 169 | @Override 170 | public String getCommandFormat() { 171 | return config.getString("discordchat.format.command"); 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/DiscordChat.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core; 2 | 3 | import net.dv8tion.jda.core.AccountType; 4 | import net.dv8tion.jda.core.JDA; 5 | import net.dv8tion.jda.core.JDABuilder; 6 | import net.dv8tion.jda.core.entities.*; 7 | import net.dv8tion.jda.core.exceptions.ErrorResponseException; 8 | import net.dv8tion.jda.core.exceptions.RateLimitedException; 9 | import net.dv8tion.jda.core.requests.RestAction; 10 | import net.shadowfacts.discordchat.api.*; 11 | import net.shadowfacts.discordchat.api.command.ICommandManager; 12 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 13 | import net.shadowfacts.discordchat.core.command.CommandManager; 14 | import net.shadowfacts.discordchat.core.command.impl.meta.CommandCommands; 15 | import net.shadowfacts.discordchat.core.command.impl.meta.CommandHelp; 16 | import net.shadowfacts.discordchat.core.command.impl.meta.CommandReload; 17 | import net.shadowfacts.discordchat.core.command.impl.meta.CommandRoleID; 18 | import net.shadowfacts.discordchat.core.command.impl.minecraft.*; 19 | import net.shadowfacts.discordchat.core.command.impl.permissions.CommandPermission; 20 | import net.shadowfacts.discordchat.core.command.impl.permissions.CommandSetPermission; 21 | import net.shadowfacts.discordchat.core.permission.PermissionManager; 22 | import net.shadowfacts.discordchat.core.util.QueuedMessage; 23 | 24 | import javax.security.auth.login.LoginException; 25 | import java.io.File; 26 | import java.io.IOException; 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | import java.util.concurrent.LinkedBlockingQueue; 30 | import java.util.stream.Collectors; 31 | 32 | /** 33 | * @author shadowfacts 34 | */ 35 | public class DiscordChat implements IDiscordChat { 36 | 37 | private IMinecraftAdapter minecraftAdapter; 38 | 39 | private Logger logger; 40 | private IConfig config; 41 | private PermissionManager permissionManager; 42 | private CommandManager commandManager; 43 | private MessageFormatter formatter; 44 | 45 | private boolean running; 46 | 47 | private JDA jda; 48 | 49 | private final LinkedBlockingQueue sendQueue = new LinkedBlockingQueue<>(); 50 | private List channels; 51 | 52 | public DiscordChat(File dcDir, IMinecraftAdapter minecraftAdapter) throws IOException { 53 | this.minecraftAdapter = minecraftAdapter; 54 | 55 | if (!dcDir.exists()) { 56 | dcDir.mkdirs(); 57 | } 58 | 59 | logger = new Logger(); 60 | 61 | config = new Config(new File(dcDir, "DiscordChat.conf")); 62 | permissionManager = new PermissionManager(this, new File(dcDir, "permissions.json")); 63 | commandManager = new CommandManager(this); 64 | formatter = new MessageFormatter(this); 65 | 66 | commandManager.register(new CommandHelp(this)); 67 | commandManager.register(new CommandCommands(this)); 68 | commandManager.register(new CommandRoleID(this)); 69 | commandManager.register(new CommandReload(this)); 70 | commandManager.register(new CommandOnline(this)); 71 | commandManager.register(new CommandTPS(this)); 72 | commandManager.register(new CommandExecute(this)); 73 | commandManager.register(new CommandUnstick(this)); 74 | commandManager.register(new CommandTime(this)); 75 | commandManager.register(new CommandTell(this)); 76 | commandManager.register(new CommandPermission(this)); 77 | commandManager.register(new CommandSetPermission(this)); 78 | } 79 | 80 | @Override 81 | public void connect() { 82 | String token = config.getToken(); 83 | if (token == null || token.isEmpty()) { 84 | logger.bigWarning("No token provided, DiscordChat automatically disabled."); 85 | return; 86 | } 87 | 88 | new Thread(() -> { 89 | try { 90 | jda = new JDABuilder(AccountType.BOT) 91 | .setToken(config.getToken()) 92 | .addEventListener(new Listener(this)) 93 | .buildBlocking(); 94 | } catch (LoginException | IllegalArgumentException e) { 95 | throw new RuntimeException("Invalid Discord token. Please verify your token in the DiscordChat config file.", e); 96 | } catch (Exception e) { 97 | throw new RuntimeException(e); 98 | } 99 | running = true; 100 | 101 | Guild guild = jda.getGuildById(config.getServerID()); 102 | if (guild == null) { 103 | throw new RuntimeException("Invalid server ID"); 104 | } 105 | if (config.getChannelIDs().isEmpty()) { 106 | throw new RuntimeException("No channel IDs"); 107 | } else { 108 | for (long channelID : config.getChannelIDs()) { 109 | if (guild.getTextChannelById(channelID) == null) { 110 | throw new RuntimeException("Invalid channel ID: " + channelID); 111 | } 112 | } 113 | } 114 | }, "DiscordChat-initializer").start(); 115 | } 116 | 117 | @Override 118 | public void start() { 119 | if (running) { 120 | 121 | Thread thread = new Thread(this::sendQueueThread, "DiscordChat-send-queue"); 122 | thread.setUncaughtExceptionHandler((thrd, t) -> { 123 | logger.error(t, "Uncaught exception in DiscordChat-send-queue thread"); 124 | }); 125 | thread.start(); 126 | } 127 | } 128 | 129 | private void sendQueueThread() { 130 | try { 131 | while(jda == null) Thread.sleep(50); // Don't hog the CPU 132 | } catch (InterruptedException e) { 133 | e.printStackTrace(); 134 | } 135 | 136 | QueuedMessage candidate = null; 137 | while (running) { 138 | if (jda.getStatus() == JDA.Status.SHUTTING_DOWN || jda.getStatus() == JDA.Status.SHUTDOWN) { 139 | sendQueue.clear(); 140 | running = false; 141 | break; 142 | } 143 | 144 | try { 145 | if (candidate == null) 146 | candidate = sendQueue.take(); 147 | 148 | RestAction result = candidate.send(); 149 | result.complete(true); 150 | candidate = null; // Upon a successful result, reset the candidate 151 | } catch (InterruptedException e) { 152 | e.printStackTrace(); // Since we're not explicitly invoking Thread interrupt, this should never occur. 153 | } catch (RateLimitedException e) { 154 | logger.debug("Message was rate limited, will try again in " + e.getRetryAfter()); 155 | try { 156 | Thread.sleep(e.getRetryAfter()); 157 | } catch (InterruptedException ex) { 158 | ex.printStackTrace(); 159 | } 160 | } catch (ErrorResponseException e) { 161 | logger.error(e, "Error sending message to Discord: " + e.getErrorResponse().getCode() + " (" + e.getErrorResponse().getMeaning() + ")"); 162 | } catch (InsufficientPermissionException e) { 163 | logger.error(e, "Error sending message to Discord: " + e.getMessage()); 164 | } 165 | } 166 | } 167 | 168 | @Override 169 | public void stop() { 170 | running = false; 171 | jda.shutdown(); 172 | } 173 | 174 | @Override 175 | public ILogger getLogger() { 176 | return logger; 177 | } 178 | 179 | @Override 180 | public IConfig getConfig() { 181 | return config; 182 | } 183 | 184 | @Override 185 | public ICommandManager getCommandManager() { 186 | return commandManager; 187 | } 188 | 189 | @Override 190 | public IPermissionManager getPermissionManager() { 191 | return permissionManager; 192 | } 193 | 194 | @Override 195 | public IMessageFormatter getFormatter() { 196 | return formatter; 197 | } 198 | 199 | @Override 200 | public JDA getJDA() { 201 | return jda; 202 | } 203 | 204 | @Override 205 | public IMinecraftAdapter getMinecraftAdapter() { 206 | return minecraftAdapter; 207 | } 208 | 209 | @Override 210 | public void sendMessage(String message, List channels) { 211 | if (channels.isEmpty()) { 212 | throw new NullPointerException("channels cannot be empty"); 213 | } 214 | if (!running) return; 215 | if (message == null || message.isEmpty()) return; 216 | 217 | 218 | List users = jda.getGuildById(config.getServerID()).getMembers().stream() 219 | .map(Member::getUser) 220 | .collect(Collectors.toList()); 221 | String temp = message.toLowerCase(); 222 | for (User user : users) { 223 | if (temp.contains("@" + user.getName().toLowerCase())) { 224 | message = message.replaceAll("(?i)@" + user.getName(), user.getAsMention()); 225 | } 226 | } 227 | 228 | for (MessageChannel channel : channels) { 229 | sendQueue.add(new QueuedMessage(message, channel)); 230 | } 231 | } 232 | 233 | @Override 234 | public void sendMessage(String message) { 235 | if (!running) return; 236 | 237 | if (channels == null) { 238 | try { 239 | while (jda == null) Thread.sleep(50); 240 | } catch (InterruptedException e) { 241 | e.printStackTrace(); 242 | } 243 | Guild guild = jda.getGuildById(config.getServerID()); 244 | channels = new ArrayList<>(); 245 | for (long channelID : config.getChannelIDs()) { 246 | channels.add(guild.getTextChannelById(channelID)); 247 | } 248 | } 249 | sendMessage(message, channels); 250 | } 251 | 252 | @Override 253 | public String filterMCMessage(String message) { 254 | switch (config.getMCMessageFilterMode()) { 255 | case PREFIX: 256 | if (!message.startsWith(config.getMCMessageFilter())) { 257 | return null; 258 | } else { 259 | if (config.stripFilterPart()) { 260 | return message.substring(config.getMCMessageFilter().length(), message.length()); 261 | } else { 262 | return message; 263 | } 264 | } 265 | case SUFFIX: 266 | if (!message.endsWith(config.getMCMessageFilter())) { 267 | return null; 268 | } else { 269 | if (config.stripFilterPart()) { 270 | return message.substring(0, message.length() - config.getMCMessageFilter().length()); 271 | } else { 272 | return message; 273 | } 274 | } 275 | default: 276 | case NONE: 277 | return message; 278 | } 279 | } 280 | 281 | } 282 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/Listener.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core; 2 | 3 | import com.vdurmont.emoji.EmojiParser; 4 | import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent; 5 | import net.dv8tion.jda.core.events.message.priv.PrivateMessageReceivedEvent; 6 | import net.dv8tion.jda.core.hooks.ListenerAdapter; 7 | import net.shadowfacts.discordchat.api.IConfig; 8 | import net.shadowfacts.discordchat.api.IDiscordChat; 9 | import net.shadowfacts.discordchat.api.IMessageFormatter; 10 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 11 | import net.shadowfacts.discordchat.api.command.ICommandManager; 12 | import net.shadowfacts.discordchat.core.util.NearestMCColor; 13 | 14 | import java.awt.*; 15 | 16 | /** 17 | * @author shadowfacts 18 | */ 19 | public class Listener extends ListenerAdapter { 20 | 21 | private IConfig config; 22 | private ICommandManager commandManager; 23 | private IMessageFormatter formatter; 24 | private IMinecraftAdapter minecraftAdapter; 25 | 26 | public Listener(IDiscordChat discordChat) { 27 | config = discordChat.getConfig(); 28 | commandManager = discordChat.getCommandManager(); 29 | formatter = discordChat.getFormatter(); 30 | minecraftAdapter = discordChat.getMinecraftAdapter(); 31 | } 32 | 33 | @Override 34 | public void onPrivateMessageReceived(PrivateMessageReceivedEvent event) { 35 | String raw = event.getMessage().getRawContent(); 36 | if (raw.startsWith(config.getCommandPrefix())) { 37 | commandManager.execute(raw.substring(config.getCommandPrefix().length()), event.getAuthor(), event.getChannel()); 38 | } 39 | } 40 | 41 | @Override 42 | public void onGuildMessageReceived(GuildMessageReceivedEvent event) { 43 | if (event.getAuthor().equals(event.getJDA().getSelfUser())) return; 44 | if (event.getGuild().getIdLong() != config.getServerID()) return; 45 | if (!config.getChannelIDs().contains(event.getChannel().getIdLong())) return; 46 | 47 | String raw = event.getMessage().getRawContent(); 48 | if (raw.startsWith(config.getCommandPrefix())) { 49 | commandManager.execute(raw.substring(config.getCommandPrefix().length()), event.getAuthor(), event.getChannel()); 50 | } else { 51 | String channel = event.getChannel().getName(); 52 | String color = event.getMember().getColor() == null ? config.getDefaultColor() : NearestMCColor.get(event.getMember().getColor().getRGB()); 53 | String author = event.getMember().getEffectiveName().replaceAll("\\.", "_"); 54 | String message = EmojiParser.parseToAliases(event.getMessage().getContent()); 55 | minecraftAdapter.sendMessage(formatter.fromDiscord(channel, color, author, message)); 56 | event.getMessage().getAttachments().forEach(it -> { 57 | minecraftAdapter.sendMessage("Attachment: " + it.getUrl()); 58 | }); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/Logger.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core; 2 | 3 | import net.shadowfacts.discordchat.api.ILogger; 4 | import org.apache.logging.log4j.LogManager; 5 | 6 | import java.util.Collections; 7 | 8 | /** 9 | * @author shadowfacts 10 | */ 11 | public class Logger implements ILogger { 12 | 13 | private org.apache.logging.log4j.Logger logger = LogManager.getFormatterLogger("discordchat"); 14 | 15 | @Override 16 | public void debug(String msg, Object... args) { 17 | logger.debug(msg, args); 18 | } 19 | 20 | @Override 21 | public void info(String msg, Object... args) { 22 | logger.info(msg, args); 23 | } 24 | 25 | @Override 26 | public void warn(String msg, Object... args) { 27 | logger.warn(msg, args); 28 | } 29 | 30 | @Override 31 | public void warn(Throwable t, String msg, Object... args) { 32 | logger.warn(String.format(msg, args), t); 33 | } 34 | 35 | @Override 36 | public void bigWarning(String msg) { 37 | String sep = String.join("", Collections.nCopies(msg.length(), "*")); 38 | warn(sep); 39 | warn(msg); 40 | warn(sep); 41 | } 42 | 43 | @Override 44 | public void error(String msg, Object... args) { 45 | logger.error(msg, args); 46 | } 47 | 48 | @Override 49 | public void error(Throwable t, String msg, Object... args) { 50 | logger.error(String.format(msg, args), t); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/MessageFormatter.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core; 2 | 3 | import net.shadowfacts.discordchat.api.IConfig; 4 | import net.shadowfacts.discordchat.api.IDiscordChat; 5 | import net.shadowfacts.discordchat.api.IMessageFormatter; 6 | 7 | /** 8 | * @author shadowfacts 9 | */ 10 | public class MessageFormatter implements IMessageFormatter { 11 | 12 | private IConfig config; 13 | 14 | public MessageFormatter(IDiscordChat discordChat) { 15 | this.config = discordChat.getConfig(); 16 | } 17 | 18 | @Override 19 | public String fromMC(String sender, String message) { 20 | return config.getFromMCFormat() 21 | .replace("$1", sender) 22 | .replace("$2", message); 23 | } 24 | 25 | @Override 26 | public String fromDiscord(String channel, String senderColor, String sender, String message) { 27 | return config.getFromDiscordFormat() 28 | .replace("$1", channel) 29 | .replace("$2", sender) 30 | .replace("$3", message) 31 | .replace("$4", senderColor); 32 | } 33 | 34 | @Override 35 | public String fromMCPrivate(String sender, String message) { 36 | return config.getFromMCPrivateFormat() 37 | .replace("$1", sender) 38 | .replace("$2", message); 39 | } 40 | 41 | @Override 42 | public String fromDiscordPrivate(String sender, String message) { 43 | return config.getFromDiscordPrivateFormat() 44 | .replace("$1", sender) 45 | .replace("$2", message); 46 | } 47 | 48 | @Override 49 | public String death(String player, String message) { 50 | return config.getDeathFormat() 51 | .replace("$1", player) 52 | .replace("$2", message); 53 | } 54 | 55 | @Override 56 | public String achievement(String player, String achievement) { 57 | return config.getAchievementFormat() 58 | .replace("$1", player) 59 | .replace("$2", achievement); 60 | } 61 | 62 | @Override 63 | public String join(String player) { 64 | return config.getJoinFormat() 65 | .replace("$1", player); 66 | } 67 | 68 | @Override 69 | public String leave(String player) { 70 | return config.getLeaveFormat() 71 | .replace("$1", player); 72 | } 73 | 74 | @Override 75 | public String command(String message) { 76 | return config.getCommandFormat() 77 | .replace("$1", message); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/CommandManager.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command; 2 | 3 | import net.dv8tion.jda.core.entities.Guild; 4 | import net.dv8tion.jda.core.entities.MessageChannel; 5 | import net.dv8tion.jda.core.entities.User; 6 | import net.shadowfacts.discordchat.api.IConfig; 7 | import net.shadowfacts.discordchat.api.IDiscordChat; 8 | import net.shadowfacts.discordchat.api.command.ICommand; 9 | import net.shadowfacts.discordchat.api.command.ICommandManager; 10 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 11 | import net.shadowfacts.discordchat.api.permission.Permission; 12 | import net.shadowfacts.discordchat.core.command.exception.InvalidPermissionException; 13 | 14 | import java.util.*; 15 | 16 | /** 17 | * @author shadowfacts 18 | */ 19 | public class CommandManager implements ICommandManager { 20 | 21 | private IDiscordChat discordChat; 22 | private IConfig config; 23 | 24 | private Map commands = new HashMap<>(); 25 | 26 | public CommandManager(IDiscordChat discordChat) { 27 | this.discordChat = discordChat; 28 | config = discordChat.getConfig(); 29 | } 30 | 31 | @Override 32 | public IDiscordChat getDiscordChat() { 33 | return discordChat; 34 | } 35 | 36 | @Override 37 | public void register(ICommand command) { 38 | commands.put(command.getName().toLowerCase(), command); 39 | } 40 | 41 | @Override 42 | public boolean exists(String name) { 43 | return commands.containsKey(name.toLowerCase()); 44 | } 45 | 46 | @Override 47 | public ICommand get(String name) { 48 | return commands.get(name.toLowerCase()); 49 | } 50 | 51 | @Override 52 | public void execute(String message, User sender, MessageChannel channel) { 53 | String[] bits = message.split(" "); 54 | try { 55 | if (exists(bits[0])) { 56 | ICommand command = get(bits[0]); 57 | checkPermission(command, sender, discordChat.getJDA().getGuildById(config.getServerID())); 58 | command.execute(Arrays.copyOfRange(bits, 1, bits.length), sender, channel); 59 | } else { 60 | throw new CommandException("Unknown command: " + bits[0]); 61 | } 62 | } catch (CommandException e) { 63 | discordChat.sendMessage(discordChat.getFormatter().command("Error executing command: " + e.getLocalizedMessage()), channel); 64 | } 65 | } 66 | 67 | @Override 68 | public Collection getCommands() { 69 | return commands.values(); 70 | } 71 | 72 | private void checkPermission(ICommand command, User user, Guild guild) throws InvalidPermissionException { 73 | Permission min = command.getMinimumPermission(); 74 | Permission actual = discordChat.getPermissionManager().get(user, guild); 75 | if (!actual.has(min)) { 76 | throw new InvalidPermissionException(min, actual, command.getName() + " command"); 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/exception/InvalidPermissionException.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.exception; 2 | 3 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 4 | import net.shadowfacts.discordchat.api.permission.Permission; 5 | 6 | /** 7 | * @author shadowfacts 8 | */ 9 | public class InvalidPermissionException extends CommandException { 10 | 11 | public InvalidPermissionException(Permission required, Permission actual, String _for) { 12 | super(String.format("Permission %s is required for %s but user has only %s", required, _for, actual)); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/exception/InvalidUsageException.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.exception; 2 | 3 | import net.shadowfacts.discordchat.api.command.ICommand; 4 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 5 | 6 | /** 7 | * @author shadowfacts 8 | */ 9 | public class InvalidUsageException extends CommandException { 10 | 11 | public InvalidUsageException(ICommand command) { 12 | super("Invalid use of " + command.getName() + ". Usage: " + command.getName() + " " + command.getUsage()); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/meta/CommandCommands.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.meta; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IConfig; 6 | import net.shadowfacts.discordchat.api.IDiscordChat; 7 | import net.shadowfacts.discordchat.api.command.ICommand; 8 | import net.shadowfacts.discordchat.api.command.ICommandManager; 9 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 10 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 11 | import net.shadowfacts.discordchat.api.permission.Permission; 12 | 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | /** 17 | * @author shadowfacts 18 | */ 19 | public class CommandCommands implements ICommand { 20 | 21 | private IDiscordChat discordChat; 22 | private IConfig config; 23 | private ICommandManager commandManager; 24 | private IPermissionManager permissionManager; 25 | 26 | public CommandCommands(IDiscordChat discordChat) { 27 | this.discordChat = discordChat; 28 | this.config = discordChat.getConfig(); 29 | this.commandManager = discordChat.getCommandManager(); 30 | this.permissionManager = discordChat.getPermissionManager(); 31 | } 32 | 33 | @Override 34 | public String getName() { 35 | return "commands"; 36 | } 37 | 38 | @Override 39 | public Permission getMinimumPermission() { 40 | return config.getMinimumPermission(getName()); 41 | } 42 | 43 | @Override 44 | public void execute(String[] args, User author, MessageChannel channel) throws CommandException { 45 | List commands = commandManager.getCommands().stream() 46 | .map(cmd -> { 47 | Permission min = cmd.getMinimumPermission(); 48 | if (permissionManager.has(author, discordChat.getJDA().getGuildById(config.getServerID()), min)) { 49 | return "**" + cmd.getName() + "**"; 50 | } else { 51 | return cmd.getName(); 52 | } 53 | }) 54 | .collect(Collectors.toList()); 55 | String msg = "Commands (you have permission for **bolded** ones):\n" + String.join(", ", commands); 56 | sendResponse(msg, channel, discordChat); 57 | } 58 | 59 | @Override 60 | public String getDescription() { 61 | return "Lists all available commands"; 62 | } 63 | 64 | @Override 65 | public String getUsage() { 66 | return ""; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/meta/CommandHelp.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.meta; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IConfig; 6 | import net.shadowfacts.discordchat.api.IDiscordChat; 7 | import net.shadowfacts.discordchat.api.command.ICommand; 8 | import net.shadowfacts.discordchat.api.command.ICommandManager; 9 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 10 | import net.shadowfacts.discordchat.api.permission.Permission; 11 | import net.shadowfacts.discordchat.core.command.exception.InvalidUsageException; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * @author shadowfacts 17 | */ 18 | public class CommandHelp implements ICommand { 19 | 20 | private IDiscordChat discordChat; 21 | private IConfig config; 22 | private ICommandManager commandManager; 23 | 24 | public CommandHelp(IDiscordChat discordChat) { 25 | this.discordChat = discordChat; 26 | this.config = discordChat.getConfig(); 27 | this.commandManager = discordChat.getCommandManager(); 28 | } 29 | 30 | @Override 31 | public String getName() { 32 | return "help"; 33 | } 34 | 35 | @Override 36 | public Permission getMinimumPermission() { 37 | return config.getMinimumPermission(getName()); 38 | } 39 | 40 | @Override 41 | public void execute(String[] args, User author, MessageChannel channel) throws CommandException { 42 | if (args.length < 1) { 43 | throw new InvalidUsageException(this); 44 | } 45 | if (commandManager.exists(args[0])) { 46 | List lines = commandManager.get(args[0]).handleHelp(author, channel); 47 | String msg = String.join("\n", lines); 48 | sendResponse(msg, channel, discordChat); 49 | } else { 50 | throw new CommandException("No such command: " + args[0]); 51 | } 52 | } 53 | 54 | @Override 55 | public String getDescription() { 56 | return "Retrieves help for the given command."; 57 | } 58 | 59 | @Override 60 | public String getUsage() { 61 | return ""; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/meta/CommandReload.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.meta; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IConfig; 6 | import net.shadowfacts.discordchat.api.IDiscordChat; 7 | import net.shadowfacts.discordchat.api.command.ICommand; 8 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 9 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 10 | import net.shadowfacts.discordchat.api.permission.Permission; 11 | 12 | import java.io.IOException; 13 | 14 | /** 15 | * @author shadowfacts 16 | */ 17 | public class CommandReload implements ICommand { 18 | 19 | private IDiscordChat discordChat; 20 | private IConfig config; 21 | private IPermissionManager permissionManager; 22 | 23 | public CommandReload(IDiscordChat discordChat) { 24 | this.discordChat = discordChat; 25 | config = discordChat.getConfig(); 26 | permissionManager = discordChat.getPermissionManager(); 27 | } 28 | 29 | @Override 30 | public String getName() { 31 | return "reload"; 32 | } 33 | 34 | @Override 35 | public Permission getMinimumPermission() { 36 | return config.getMinimumPermission(getName()); 37 | } 38 | 39 | @Override 40 | public void execute(String[] args, User sender, MessageChannel channel) throws CommandException { 41 | try { 42 | config.load(); 43 | permissionManager.load(); 44 | sendResponse("Configuration and permissions reloaded", channel, discordChat); 45 | } catch (IOException e) { 46 | throw new CommandException(e); 47 | } 48 | } 49 | 50 | @Override 51 | public String getDescription() { 52 | return "Reloads configuration and permissions"; 53 | } 54 | 55 | @Override 56 | public String getUsage() { 57 | return ""; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/meta/CommandRoleID.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.meta; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.Role; 5 | import net.dv8tion.jda.core.entities.User; 6 | import net.shadowfacts.discordchat.api.IConfig; 7 | import net.shadowfacts.discordchat.api.IDiscordChat; 8 | import net.shadowfacts.discordchat.api.command.ICommand; 9 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 10 | import net.shadowfacts.discordchat.api.permission.Permission; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @author shadowfacts 16 | */ 17 | public class CommandRoleID implements ICommand { 18 | 19 | private IDiscordChat discordChat; 20 | private IConfig config; 21 | 22 | public CommandRoleID(IDiscordChat discordChat) { 23 | this.discordChat = discordChat; 24 | this.config = discordChat.getConfig(); 25 | } 26 | 27 | @Override 28 | public String getName() { 29 | return "roleid"; 30 | } 31 | 32 | @Override 33 | public Permission getMinimumPermission() { 34 | return config.getMinimumPermission(getName()); 35 | } 36 | 37 | @Override 38 | public void execute(String[] args, User sender, MessageChannel channel) throws CommandException { 39 | String role = String.join(" ", args); 40 | List roles = discordChat.getJDA().getGuildById(config.getServerID()).getRolesByName(role, true); 41 | if (roles.isEmpty()) { 42 | sendResponse("No such role: " + role, channel, discordChat); 43 | } else { 44 | sendResponse("ID for role '" + roles.get(0).getName() + "' is " + roles.get(0).getId(), channel, discordChat); 45 | } 46 | } 47 | 48 | @Override 49 | public String getDescription() { 50 | return "Retrieves the ID for the role with the given name"; 51 | } 52 | 53 | @Override 54 | public String getUsage() { 55 | return ""; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/minecraft/CommandExecute.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.minecraft; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IConfig; 6 | import net.shadowfacts.discordchat.api.IDiscordChat; 7 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 8 | import net.shadowfacts.discordchat.api.command.ICommand; 9 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 10 | import net.shadowfacts.discordchat.api.permission.Permission; 11 | import net.shadowfacts.discordchat.core.command.exception.InvalidUsageException; 12 | 13 | /** 14 | * @author shadowfacts 15 | */ 16 | public class CommandExecute implements ICommand { 17 | 18 | private IConfig config; 19 | private IMinecraftAdapter minecraftAdapter; 20 | 21 | public CommandExecute(IDiscordChat discordChat) { 22 | config = discordChat.getConfig(); 23 | minecraftAdapter = discordChat.getMinecraftAdapter(); 24 | } 25 | 26 | @Override 27 | public String getName() { 28 | return "exec"; 29 | } 30 | 31 | @Override 32 | public Permission getMinimumPermission() { 33 | return config.getMinimumPermission(getName()); 34 | } 35 | 36 | @Override 37 | public void execute(String[] args, User sender, MessageChannel channel) throws CommandException { 38 | if (args.length == 0) throw new InvalidUsageException(this); 39 | 40 | String cmd = String.join(" ", args); 41 | minecraftAdapter.executeCommand(cmd); 42 | } 43 | 44 | @Override 45 | public String getDescription() { 46 | return "Executes the given command in Minecraft"; 47 | } 48 | 49 | @Override 50 | public String getUsage() { 51 | return ""; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/minecraft/CommandOnline.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.minecraft; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IConfig; 6 | import net.shadowfacts.discordchat.api.IDiscordChat; 7 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 8 | import net.shadowfacts.discordchat.api.command.ICommand; 9 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 10 | import net.shadowfacts.discordchat.api.permission.Permission; 11 | 12 | import java.util.Set; 13 | 14 | /** 15 | * @author shadowfacts 16 | */ 17 | public class CommandOnline implements ICommand { 18 | 19 | private IDiscordChat discordChat; 20 | private IConfig config; 21 | private IMinecraftAdapter minecraftAdapter; 22 | 23 | public CommandOnline(IDiscordChat discordChat) { 24 | this.discordChat = discordChat; 25 | this.config = discordChat.getConfig(); 26 | this.minecraftAdapter = discordChat.getMinecraftAdapter(); 27 | } 28 | 29 | @Override 30 | public String getName() { 31 | return "online"; 32 | } 33 | 34 | @Override 35 | public Permission getMinimumPermission() { 36 | return config.getMinimumPermission(getName()); 37 | } 38 | 39 | @Override 40 | public void execute(String[] args, User sender, MessageChannel channel) throws CommandException { 41 | Set players = minecraftAdapter.getOnlinePlayers(); 42 | int count = players.size(); 43 | String s = count == 1 ? "player" : "players"; 44 | sendResponse(count + " " + s + " online: " + String.join(", ", players), channel, discordChat); 45 | } 46 | 47 | @Override 48 | public String getDescription() { 49 | return "Lists all online players"; 50 | } 51 | 52 | @Override 53 | public String getUsage() { 54 | return ""; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/minecraft/CommandTPS.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.minecraft; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IConfig; 6 | import net.shadowfacts.discordchat.api.IDiscordChat; 7 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 8 | import net.shadowfacts.discordchat.api.command.ICommand; 9 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 10 | import net.shadowfacts.discordchat.api.permission.Permission; 11 | import net.shadowfacts.discordchat.core.DiscordChat; 12 | 13 | import java.util.stream.IntStream; 14 | import java.util.stream.Stream; 15 | 16 | /** 17 | * @author shadowfacts 18 | */ 19 | public class CommandTPS implements ICommand { 20 | 21 | private IDiscordChat discordChat; 22 | private IConfig config; 23 | private IMinecraftAdapter minecraftAdapter; 24 | 25 | public CommandTPS(IDiscordChat discordChat) { 26 | this.discordChat = discordChat; 27 | this.config = discordChat.getConfig(); 28 | minecraftAdapter = discordChat.getMinecraftAdapter(); 29 | } 30 | 31 | @Override 32 | public String getName() { 33 | return "tps"; 34 | } 35 | 36 | @Override 37 | public Permission getMinimumPermission() { 38 | return config.getMinimumPermission(getName()); 39 | } 40 | 41 | @Override 42 | public void execute(String[] args, User sender, MessageChannel channel) throws CommandException { 43 | if (args.length == 0) { 44 | StringBuilder message = new StringBuilder(); 45 | for (int dim : minecraftAdapter.getAllDimensions()) { 46 | double tickTime = minecraftAdapter.getTickTime(dim); 47 | double tps = Math.min(1000 / tickTime, 20); 48 | message.append(String.format("Dimension %d: Tick time: %.3fms TPS: %.0f\n", dim, tickTime, tps)); 49 | } 50 | sendResponse(message.toString(), channel, discordChat); 51 | } else { 52 | int dim; 53 | try { 54 | dim = Integer.parseInt(args[0]); 55 | } catch (NumberFormatException e) { 56 | throw new CommandException(e); 57 | } 58 | double tickTime = minecraftAdapter.getTickTime(dim); 59 | double tps = Math.min(tickTime / 1000, 20); 60 | sendResponse(String.format("Dimension %d: Tick time: %.3fms TPS: %.0f", dim, tickTime, tps), channel, discordChat); 61 | } 62 | } 63 | 64 | @Override 65 | public String getDescription() { 66 | return "Displays TPS for a given dimension (or all dimensions, if none is specified)"; 67 | } 68 | 69 | @Override 70 | public String getUsage() { 71 | return "[dimension]"; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/minecraft/CommandTell.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.minecraft; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IConfig; 6 | import net.shadowfacts.discordchat.api.IDiscordChat; 7 | import net.shadowfacts.discordchat.api.IMessageFormatter; 8 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 9 | import net.shadowfacts.discordchat.api.command.ICommand; 10 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 11 | import net.shadowfacts.discordchat.api.permission.Permission; 12 | import net.shadowfacts.discordchat.core.command.exception.InvalidUsageException; 13 | 14 | /** 15 | * @author shadowfacts 16 | */ 17 | public class CommandTell implements ICommand { 18 | 19 | private IConfig config; 20 | private IMessageFormatter formatter; 21 | private IMinecraftAdapter minecraftAdapter; 22 | 23 | public CommandTell(IDiscordChat discordChat) { 24 | config = discordChat.getConfig(); 25 | formatter = discordChat.getFormatter(); 26 | minecraftAdapter = discordChat.getMinecraftAdapter(); 27 | } 28 | 29 | @Override 30 | public String getName() { 31 | return "tell"; 32 | } 33 | 34 | @Override 35 | public Permission getMinimumPermission() { 36 | return config.getMinimumPermission(getName()); 37 | } 38 | 39 | @Override 40 | public void execute(String[] args, User sender, MessageChannel channel) throws CommandException { 41 | if (args.length < 2) throw new InvalidUsageException(this); 42 | String player = args[0]; 43 | String message = String.join(" ", args); 44 | minecraftAdapter.sendMessageToPlayer(formatter.fromDiscordPrivate(sender.getName(), message), player); 45 | } 46 | 47 | @Override 48 | public String getDescription() { 49 | return "Sends a private message to a player in MC"; 50 | } 51 | 52 | @Override 53 | public String getUsage() { 54 | return " "; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/minecraft/CommandTime.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.minecraft; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IConfig; 6 | import net.shadowfacts.discordchat.api.IDiscordChat; 7 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 8 | import net.shadowfacts.discordchat.api.command.ICommand; 9 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 10 | import net.shadowfacts.discordchat.api.permission.Permission; 11 | import net.shadowfacts.discordchat.core.command.exception.InvalidUsageException; 12 | 13 | /** 14 | * @author shadowfacts 15 | */ 16 | public class CommandTime implements ICommand { 17 | 18 | private IDiscordChat discordChat; 19 | private IConfig config; 20 | private IMinecraftAdapter minecraftAdapter; 21 | 22 | public CommandTime(IDiscordChat discordChat) { 23 | this.discordChat = discordChat; 24 | config = discordChat.getConfig(); 25 | minecraftAdapter = discordChat.getMinecraftAdapter(); 26 | } 27 | 28 | @Override 29 | public String getName() { 30 | return "time"; 31 | } 32 | 33 | @Override 34 | public Permission getMinimumPermission() { 35 | return config.getMinimumPermission(getName()); 36 | } 37 | 38 | @Override 39 | public void execute(String[] args, User sender, MessageChannel channel) throws CommandException { 40 | int dimension; 41 | switch (args.length) { 42 | case 0: 43 | dimension = 0; 44 | break; 45 | case 1: 46 | dimension = Integer.parseInt(args[0]); 47 | break; 48 | default: 49 | throw new InvalidUsageException(this); 50 | } 51 | 52 | long time = minecraftAdapter.getWorldTime(dimension) % 24000L; 53 | 54 | String phase = null; 55 | if (0 <= time && time < 1000) { 56 | phase = "Dawn"; 57 | } else if (1000 < time && time < 12000) { 58 | phase = "Day"; 59 | } else if (12000 < time && time < 13000) { 60 | phase = "Dusk"; 61 | } else if (13000 < time && time < 24000) { 62 | phase = "Night"; 63 | } 64 | 65 | String response = (phase == null ? "" : ("**" + phase + "**: ")) + time; 66 | sendResponse(response, channel, discordChat); 67 | } 68 | 69 | @Override 70 | public String getDescription() { 71 | return "Gets the time of day for a dimension."; 72 | } 73 | 74 | @Override 75 | public String getUsage() { 76 | return "[dimension]"; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/minecraft/CommandUnstick.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.minecraft; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IConfig; 6 | import net.shadowfacts.discordchat.api.IDiscordChat; 7 | import net.shadowfacts.discordchat.api.IMinecraftAdapter; 8 | import net.shadowfacts.discordchat.api.command.ICommand; 9 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 10 | import net.shadowfacts.discordchat.api.permission.Permission; 11 | import net.shadowfacts.discordchat.core.command.exception.InvalidUsageException; 12 | 13 | /** 14 | * @author shadowfacts 15 | */ 16 | public class CommandUnstick implements ICommand { 17 | 18 | private IDiscordChat discordChat; 19 | private IConfig config; 20 | private IMinecraftAdapter minecraftAdapter; 21 | 22 | public CommandUnstick(IDiscordChat discordChat) { 23 | this.discordChat = discordChat; 24 | this.config = discordChat.getConfig(); 25 | this.minecraftAdapter = discordChat.getMinecraftAdapter(); 26 | } 27 | 28 | @Override 29 | public String getName() { 30 | return "unstick"; 31 | } 32 | 33 | @Override 34 | public Permission getMinimumPermission() { 35 | return config.getMinimumPermission(getName()); 36 | } 37 | 38 | @Override 39 | public void execute(String[] args, User sender, MessageChannel channel) throws CommandException { 40 | if (args.length < 1) throw new InvalidUsageException(this); 41 | String error = minecraftAdapter.teleportPlayerToSpawn(args[0]); 42 | if (error == null) { // success 43 | sendResponse("Unstuck player: " + args[0], channel, discordChat); 44 | } else { 45 | sendResponse("Unable to unstick player: " + error, channel, discordChat); 46 | } 47 | } 48 | 49 | @Override 50 | public String getDescription() { 51 | return "Teleports the player with the given name to spawn"; 52 | } 53 | 54 | @Override 55 | public String getUsage() { 56 | return ""; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/permissions/CommandPermission.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.permissions; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.User; 5 | import net.shadowfacts.discordchat.api.IConfig; 6 | import net.shadowfacts.discordchat.api.IDiscordChat; 7 | import net.shadowfacts.discordchat.api.command.ICommand; 8 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 9 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 10 | import net.shadowfacts.discordchat.api.permission.Permission; 11 | 12 | /** 13 | * @author shadowfacts 14 | */ 15 | public class CommandPermission implements ICommand { 16 | 17 | private IDiscordChat discordChat; 18 | private IConfig config; 19 | private IPermissionManager permissionManager; 20 | 21 | public CommandPermission(IDiscordChat discordChat) { 22 | this.discordChat = discordChat; 23 | this.config = discordChat.getConfig(); 24 | permissionManager = discordChat.getPermissionManager(); 25 | } 26 | 27 | @Override 28 | public String getName() { 29 | return "permission"; 30 | } 31 | 32 | @Override 33 | public Permission getMinimumPermission() { 34 | return config.getMinimumPermission(getName()); 35 | } 36 | 37 | @Override 38 | public void execute(String[] args, User sender, MessageChannel channel) throws CommandException { 39 | sendResponse("Permission level for " + sender.getName() + ": " + permissionManager.get(sender, discordChat.getJDA().getGuildById(config.getServerID())), channel, discordChat); 40 | } 41 | 42 | @Override 43 | public String getDescription() { 44 | return "Retrieves the caller's permission"; 45 | } 46 | 47 | @Override 48 | public String getUsage() { 49 | return ""; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/command/impl/permissions/CommandSetPermission.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.command.impl.permissions; 2 | 3 | import net.dv8tion.jda.core.entities.MessageChannel; 4 | import net.dv8tion.jda.core.entities.Role; 5 | import net.dv8tion.jda.core.entities.User; 6 | import net.shadowfacts.discordchat.api.IConfig; 7 | import net.shadowfacts.discordchat.api.IDiscordChat; 8 | import net.shadowfacts.discordchat.api.ILogger; 9 | import net.shadowfacts.discordchat.api.command.ICommand; 10 | import net.shadowfacts.discordchat.api.command.exception.CommandException; 11 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 12 | import net.shadowfacts.discordchat.api.permission.Permission; 13 | import net.shadowfacts.discordchat.core.command.exception.InvalidUsageException; 14 | 15 | import java.io.IOException; 16 | import java.util.Arrays; 17 | import java.util.List; 18 | 19 | /** 20 | * @author shadowfacts 21 | */ 22 | public class CommandSetPermission implements ICommand { 23 | 24 | private IDiscordChat discordChat; 25 | private ILogger logger; 26 | private IConfig config; 27 | private IPermissionManager permissionManager; 28 | 29 | public CommandSetPermission(IDiscordChat discordChat) { 30 | this.discordChat = discordChat; 31 | logger = discordChat.getLogger(); 32 | config = discordChat.getConfig(); 33 | permissionManager = discordChat.getPermissionManager(); 34 | } 35 | 36 | @Override 37 | public String getName() { 38 | return "setPermission"; 39 | } 40 | 41 | @Override 42 | public Permission getMinimumPermission() { 43 | return config.getMinimumPermission(getName()); 44 | } 45 | 46 | @Override 47 | public void execute(String[] args, User sender, MessageChannel channel) throws CommandException { 48 | if (args.length < 2) { 49 | throw new InvalidUsageException(this); 50 | } 51 | 52 | Permission permission = Permission.valueOf(args[args.length - 1].toUpperCase()); 53 | String role = String.join(" ", Arrays.copyOfRange(args, 0, args.length - 1)); 54 | List roles = discordChat.getJDA().getGuildById(config.getServerID()).getRolesByName(role, true); 55 | if (roles.isEmpty()) { 56 | sendResponse("No such role: " + role, channel, discordChat); 57 | return; 58 | } 59 | 60 | permissionManager.set(roles.get(0), permission); 61 | 62 | try { 63 | permissionManager.save(); 64 | sendResponse("Permissions updated", channel, discordChat); 65 | } catch (IOException e) { 66 | logger.error(e, "Unable to save permissions"); 67 | } 68 | } 69 | 70 | @Override 71 | public String getDescription() { 72 | return "Sets the permission of the given user"; 73 | } 74 | 75 | @Override 76 | public String getUsage() { 77 | return " "; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/permission/PermissionManager.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.permission; 2 | 3 | import com.google.gson.*; 4 | import net.dv8tion.jda.core.entities.Role; 5 | import net.shadowfacts.discordchat.api.IDiscordChat; 6 | import net.shadowfacts.discordchat.api.ILogger; 7 | import net.shadowfacts.discordchat.api.permission.IPermissionManager; 8 | import net.shadowfacts.discordchat.api.permission.Permission; 9 | import net.shadowfacts.shadowlib.util.IOUtils; 10 | 11 | import java.io.*; 12 | import java.lang.reflect.Type; 13 | import java.util.HashMap; 14 | 15 | /** 16 | * @author shadowfacts 17 | */ 18 | public class PermissionManager implements IPermissionManager { 19 | 20 | private static final Gson GSON = new GsonBuilder().registerTypeAdapter(PermissionStore.class, new Deserializer()).setPrettyPrinting().create(); 21 | 22 | private static ILogger logger; 23 | 24 | private File file; 25 | private PermissionStore permissions; 26 | 27 | public PermissionManager(IDiscordChat discordChat, File file) throws IOException { 28 | logger = discordChat.getLogger(); 29 | this.file = file; 30 | load(); 31 | } 32 | 33 | @Override 34 | public Permission get(Role role) { 35 | return permissions.containsKey(role.getId()) ? permissions.get(role.getId()) : Permission.GLOBAL; 36 | } 37 | 38 | @Override 39 | public void set(Role role, Permission permission) { 40 | permissions.put(role.getId(), permission); 41 | } 42 | 43 | @Override 44 | public void load() throws IOException { 45 | if (file.exists()) { 46 | permissions = GSON.fromJson(new FileReader(file), PermissionStore.class); 47 | } else { 48 | permissions = new PermissionStore(); 49 | save(); 50 | } 51 | } 52 | 53 | @Override 54 | public void save() throws IOException { 55 | String s = new Gson().toJson(permissions); 56 | InputStream in = new ByteArrayInputStream(s.getBytes()); 57 | OutputStream out = new FileOutputStream(file); 58 | IOUtils.copy(in, out); 59 | in.close(); 60 | out.close(); 61 | } 62 | 63 | private static class PermissionStore extends HashMap { 64 | 65 | } 66 | 67 | private static class Deserializer implements JsonDeserializer { 68 | 69 | @Override 70 | public PermissionStore deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 71 | PermissionStore store = new PermissionStore(); 72 | JsonObject obj = json.getAsJsonObject(); 73 | obj.entrySet().forEach(e -> { 74 | try { 75 | store.put(e.getKey(), Permission.valueOf(e.getValue().getAsString().toUpperCase())); 76 | } catch (IllegalArgumentException ex) { 77 | logger.warn(ex, "Invalid permission '" + e.getValue().getAsString() + "' for user '" + e.getKey() + "'"); 78 | } 79 | }); 80 | return store; 81 | } 82 | 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/util/NearestMCColor.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.util; 2 | 3 | import net.shadowfacts.shadowlib.util.NearestColor; 4 | 5 | /** 6 | * @author shadowfacts 7 | */ 8 | public class NearestMCColor { 9 | 10 | public static String get(int color) { 11 | return "§" + NearestColor.find(color, Color.values(), Color::getColor).character; 12 | } 13 | 14 | private enum Color { 15 | BLACK(0x000000, '0'), 16 | DARK_BLUE(0x0000AA, '1'), 17 | DARK_GREEN(0x00AA00, '2'), 18 | DARK_AQUA(0x00AAAA, '3'), 19 | DARK_RED(0xAA0000, '4'), 20 | DARK_PURPLE(0xAA00AA, '5'), 21 | GOLD(0xFFAA00, '6'), 22 | GRAY(0xAAAAAA, '7'), 23 | DARK_GRAY(0x555555, '8'), 24 | BLUE(0x5555FF, '9'), 25 | GREEN(0x55FF55, 'a'), 26 | AQUA(0x55FFFF, 'b'), 27 | RED(0xFF5555, 'c'), 28 | LIGHT_PURPLE(0xFF55FF, 'd'), 29 | YELLOW(0xFFFF55, 'e'), 30 | WHITE(0xFFFFFF, 'f'); 31 | 32 | public final int color; 33 | public final char character; 34 | 35 | Color(int color, char character) { 36 | this.color = color; 37 | this.character = character; 38 | } 39 | 40 | public int getColor() { 41 | return color; 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/net/shadowfacts/discordchat/core/util/QueuedMessage.java: -------------------------------------------------------------------------------- 1 | package net.shadowfacts.discordchat.core.util; 2 | 3 | import net.dv8tion.jda.core.entities.Message; 4 | import net.dv8tion.jda.core.entities.MessageChannel; 5 | import net.dv8tion.jda.core.requests.RestAction; 6 | 7 | /** 8 | * @author shadowfacts 9 | */ 10 | public class QueuedMessage { 11 | 12 | private final String message; 13 | private final MessageChannel channel; 14 | 15 | public QueuedMessage(String message, MessageChannel channel) { 16 | this.message = message; 17 | this.channel = channel; 18 | } 19 | 20 | public String getMessage() { 21 | return message; 22 | } 23 | 24 | public MessageChannel getChannel() { 25 | return channel; 26 | } 27 | 28 | public RestAction send() { 29 | return channel.sendMessage(message); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/resources/assets/discordchat/default.conf: -------------------------------------------------------------------------------- 1 | discordchat { 2 | discord { 3 | token = "" 4 | server = "" 5 | channels = [] 6 | } 7 | 8 | commands { 9 | prefix = "!" 10 | 11 | permission { 12 | commands = "global" 13 | help = "global" 14 | reload = "admin" 15 | roleid = "global" 16 | exec = "admin" 17 | online = "global" 18 | tps = "global" 19 | unstick = "approved" 20 | time = "global" 21 | permission = "global" 22 | setPermission = "admin" 23 | tell = "global" 24 | } 25 | } 26 | 27 | relay { 28 | deaths = true 29 | achievements = true 30 | joinleave = true 31 | onlineoffline = true 32 | } 33 | 34 | filter { 35 | mode = "none" 36 | filter = "" 37 | stripFilter = false 38 | } 39 | 40 | format { 41 | # Default color if the role of the user has no color on Discord (eg. @everyone) 42 | defaultColor = "§f" 43 | 44 | # Format for a normal message from MC to Discord. 45 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 46 | fromMC = "MC » <$1> $2" 47 | 48 | # Format for a normal message from Discord to MC. 49 | # $1 will be replaced with the channel, $2 will be replaced with the sender's username, $3 will be replaced with the message, and $4 will be replaced with the MC color (e.g. §3) closest to the Discord user's color. 50 | fromDiscord = "#$1 » <$4$2§f> $3" 51 | 52 | # Format for a private message from MC to Discord. 53 | # $1 will be replaced with the sender's username and $2 will be replaced with the message. 54 | fromMCPrivate = "MC » <$1> $2" 55 | 56 | # Format for a private message from Discord to MC. 57 | # $1 will be replaced with the sender's username and $2 will be replaced with the message 58 | fromDiscordPrivate = "Discord » <$1> $2" 59 | 60 | # Format for a player death message from MC to Discord. 61 | # $1 will be replaced with the player's username and $2 will be replaced with the death message 62 | death = "MC » $2" 63 | 64 | # Format for a player achievement message from MC to Discord. 65 | # $1 will be replaced with the player's username and $2 will be replaced with the achievement 66 | achievement = "MC » $1 has just earned the achievement $2" 67 | 68 | # Format for a player join message from MC to Discord. 69 | # $1 will be replaced with the player's username 70 | join = "MC » $1 joined the game" 71 | 72 | # Format for a player leave message from MC to Discord. 73 | # $1 will be replaced with the player's username 74 | leave = "MC » $1 left the game" 75 | 76 | # Format applied to every command response message. 77 | # Used for differentiating command responses from multiple bot instances. 78 | # $1 will be replaced with the message 79 | command = "$1" 80 | } 81 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | mod_version = 2.3.0 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowfacts/DiscordChat/07a22edd374ef8007421b1d0601e565c3180c2a3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Nov 26 16:54:23 EST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 165 | if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then 166 | cd "$(dirname "$0")" 167 | fi 168 | 169 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 170 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include "core", "1.12.2", "1.11.2", "1.10.2", "1.8.9", "1.7.10" 2 | --------------------------------------------------------------------------------