package com.example.addon.modules; import com.example.addon.QuinnAddon; import meteordevelopment.meteorclient.events.game.ReceiveMessageEvent; import meteordevelopment.meteorclient.settings.BoolSetting; import meteordevelopment.meteorclient.settings.EnumSetting; import meteordevelopment.meteorclient.settings.IntSetting; import meteordevelopment.meteorclient.settings.Setting; import meteordevelopment.meteorclient.settings.SettingGroup; import meteordevelopment.meteorclient.settings.StringListSetting; import meteordevelopment.meteorclient.settings.StringSetting; import meteordevelopment.meteorclient.systems.friends.Friend; import meteordevelopment.meteorclient.systems.friends.Friends; import meteordevelopment.meteorclient.systems.modules.Module; import meteordevelopment.meteorclient.utils.player.ChatUtils; import meteordevelopment.orbit.EventHandler; import net.minecraft.client.network.PlayerListEntry; import net.minecraft.text.Text; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; /** * Accepts teleport requests. * *

Changes from the version this was built from: *

*/ public class AutoAccept extends Module { private final SettingGroup sgGeneral = settings.getDefaultGroup(); public enum Mode { Friends, Whitelist, Blacklist, All } private final Setting mode = sgGeneral.add(new EnumSetting.Builder() .name("mode") .description("Which players to accept requests from.") .defaultValue(Mode.Friends) .build()); private final Setting> whitelist = sgGeneral.add(new StringListSetting.Builder() .name("whitelist") .description("List of players to accept.") .visible(() -> mode.get() == Mode.Whitelist) .build()); private final Setting> blacklist = sgGeneral.add(new StringListSetting.Builder() .name("blacklist") .description("List of players to ignore.") .visible(() -> mode.get() == Mode.Blacklist) .build()); private final Setting acceptCommand = sgGeneral.add(new StringSetting.Builder() .name("accept-command") .description("Command sent to accept, with the player name appended. Servers differ: /tpy, /tpaccept, /tpayes.") .defaultValue("/tpy") .build()); private final Setting cooldown = sgGeneral.add(new IntSetting.Builder() .name("cooldown") .description("Seconds to wait before answering the same player again. Servers re-announce pending requests.") .defaultValue(30) .min(0) .sliderRange(0, 120) .build()); private final Setting requireOnline = sgGeneral.add(new BoolSetting.Builder() .name("require-online-player") .description("Only accept when the name in the message belongs to someone on the player list. Turn this off only if the server hides the list.") .defaultValue(true) .build()); private final Setting ignoreChat = sgGeneral.add(new BoolSetting.Builder() .name("ignore-player-chat") .description("Ignore messages that look like somebody talking. Without this, anyone can make you accept by typing the request text.") .defaultValue(true) .build()); private final Map lastAccepted = new HashMap<>(); public AutoAccept() { super(QuinnAddon.CATEGORY, "auto-accept", "Automatically accepts teleport requests."); } @Override public void onActivate() { lastAccepted.clear(); } @EventHandler private void onMessageReceive(ReceiveMessageEvent event) { if (mc.player == null) return; Text message = event.getMessage(); String text = message.getString(); if (!isRequest(text)) return; if (ignoreChat.get() && looksLikePlayerChat(text)) return; String playerName = extractPlayerName(text); if (playerName == null) return; if (playerName.equalsIgnoreCase(mc.player.getGameProfile().name())) return; if (!shouldAccept(playerName)) return; long now = System.currentTimeMillis(); Long last = lastAccepted.get(playerName.toLowerCase(Locale.ROOT)); if (last != null && now - last < cooldown.get() * 1000L) return; lastAccepted.put(playerName.toLowerCase(Locale.ROOT), now); info("Accepting teleport request from " + playerName); ChatUtils.sendPlayerMsg(acceptCommand.get() + " " + playerName); } private boolean isRequest(String text) { return text.contains("has requested to teleport") || text.contains("to teleport to you") || text.contains("has invited you to join them"); } /** * Server announcements are not spoken by anyone, so the usual chat shapes are a good enough * tell: {@code ...}, {@code Name: ...} and {@code [Rank] Name » ...}. A server with an * unusual chat format could still slip through, which is why the name is checked as well. */ private boolean looksLikePlayerChat(String text) { int phrase = text.indexOf("has requested"); if (phrase < 0) phrase = text.indexOf("to teleport to you"); if (phrase < 0) phrase = text.indexOf("has invited you"); if (phrase < 0) phrase = text.length(); String before = text.substring(0, phrase); return before.contains("<") || before.contains(":") || before.contains("»") || before.contains(">"); } private boolean shouldAccept(String playerName) { switch (mode.get()) { case Friends: for (Friend friend : Friends.get()) { if (friend.name.equalsIgnoreCase(playerName)) return true; } return false; case Whitelist: for (String name : whitelist.get()) { if (name.equalsIgnoreCase(playerName)) return true; } return false; case Blacklist: for (String name : blacklist.get()) { if (name.equalsIgnoreCase(playerName)) return false; } return true; case All: return true; default: return false; } } /** * Picks the name out of the announcement by looking for a word that belongs to someone on the * server, which survives rank prefixes and colour codes. Falls back to the first word when the * player list cannot be trusted. */ private String extractPlayerName(String text) { String[] parts = text.split("[^A-Za-z0-9_]+"); if (mc.getNetworkHandler() != null) { for (String part : parts) { if (part.length() < 3 || part.length() > 16) continue; for (PlayerListEntry entry : mc.getNetworkHandler().getPlayerList()) { String name = entry.getProfile().name(); if (name != null && name.equalsIgnoreCase(part)) return name; } } } if (requireOnline.get()) return null; for (String part : parts) { if (!part.isEmpty()) return part; } return null; } }