BungeeMain / src / main / java / com / lifeknight / relaymcbungeemain / utilities / BotUtils.java
BotUtils.java
Raw
package com.lifeknight.relaymcbungeemain.utilities;

import com.lifeknight.relaymcbungeemain.Main;
import com.lifeknight.relaymcbungeemain.player.SmartPlayer;
import com.lifeknight.relayutils.RelayUtils;
import com.lifeknight.relayutils.basic.Miscellaneous;
import com.lifeknight.relayutils.basic.Text;
import com.lifeknight.relayutils.player.Group;
import com.lifeknight.relayutils.player.data.ProfileColumn;
import com.lifeknight.relayutils.player.data.SmartDatabase;
import com.lifeknight.relayutils.player.punishments.Punishment;
import com.lifeknight.relayutils.utilities.ComponentBuilder;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.channel.Channel;
import net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.events.GenericEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveEvent;
import net.dv8tion.jda.api.events.session.ReadyEvent;
import net.dv8tion.jda.api.hooks.EventListener;
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
import net.dv8tion.jda.api.requests.GatewayIntent;
import net.dv8tion.jda.api.utils.ChunkingFilter;
import net.dv8tion.jda.api.utils.MemberCachePolicy;
import net.dv8tion.jda.api.utils.cache.CacheFlag;
import org.bukkit.ChatColor;

import java.awt.*;
import java.sql.ResultSet;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class BotUtils {
    private static final String SECRET = "jpOOO1vLcqpJcZQxupcrbrmfBtsut5RZ";
    public static final String TOKEN = "MTEzNzUyNDcyNDcwMDgxMTMxNQ.GLMeyR.2bwnPNxszMSLiR4_ZYZ2JObwDo3jc6iuvYzuss";
    private static final JDA BOT;

    private static final long TESTING_SERVER_ID = 1091870008449179698L;
    private static final long SERVER_ID = 792547735919132713L;

    static {
        JDABuilder builder = JDABuilder.createDefault(TOKEN);

        EventListener listener = BotUtils::processEvent;

        builder.addEventListeners(listener);

        builder.disableCache(CacheFlag.MEMBER_OVERRIDES, CacheFlag.VOICE_STATE);

        builder.setBulkDeleteSplittingEnabled(false);

        builder.setActivity(Activity.playing("relaymc.net"));

        builder.disableCache(CacheFlag.ACTIVITY);

        builder.setMemberCachePolicy(MemberCachePolicy.VOICE.or(MemberCachePolicy.OWNER));

        builder.setChunkingFilter(ChunkingFilter.NONE);

        builder.disableIntents(GatewayIntent.GUILD_PRESENCES, GatewayIntent.GUILD_MESSAGE_TYPING);

        builder.enableIntents(GatewayIntent.MESSAGE_CONTENT, GatewayIntent.GUILD_MEMBERS);

        builder.setLargeThreshold(50);

        BOT = builder.build();
    }

    private static void processEvent(GenericEvent genericEvent) {
        if (genericEvent instanceof ReadyEvent) {
            info("Discord bot up!");
            onReady((ReadyEvent) genericEvent);
        } else if (genericEvent instanceof MessageReceivedEvent) {
            onMessageReceived((MessageReceivedEvent) genericEvent);
        } else if (genericEvent instanceof MessageUpdateEvent) {

        } else if (genericEvent instanceof MessageReactionAddEvent) {

        } else if (genericEvent instanceof MessageReactionRemoveEvent) {

        } else if (genericEvent instanceof SlashCommandInteractionEvent) {
            onSlashCommand((SlashCommandInteractionEvent) genericEvent);
        }
    }

    private static void onSlashCommand(SlashCommandInteractionEvent event) {
        if (event.getName().equals("link")) {
            String code = event.getOption("code") == null ? null : event.getOption("code").getAsString();

            if (code != null) {
                UUID player = CODE_TO_PLAYER.remove(code);

                if (player != null) {
                    SmartPlayer smartPlayer = SmartPlayer.getOrCreate(player);
                    long idLong = event.getUser().getIdLong();
                    smartPlayer.setDiscordId(idLong);
                    ID_TO_UUID.put(idLong, smartPlayer.getUUID());
                    event.reply("You have successfully linked your Discord account!\n" + smartPlayer.getName()).setEphemeral(true).queue();
                    smartPlayer.sendSuccessMessage("You have successfully linked your Discord account: %s%s", ChatColor.GOLD, event.getUser().getName());
                } else {
                    event.reply("Code \"" + code + "\" not found. Try /discord link in-game again.").setEphemeral(true).queue();
                }
            } else {
                event.reply("/link [code]").setEphemeral(true).queue();
            }
        }
    }

    private static final List<String> REWARD_CHANNELS = Miscellaneous.getList("general", "suggestions", "spam");

    private static void onMessageReceived(MessageReceivedEvent event) {
        if (event.getMessage().getType() == MessageType.DEFAULT) {
            String message = event.getMessage().getContentStripped();
            if (event.isFromGuild() && event.getGuild().getId().equals(String.valueOf(getServerID()))) {
                Channel channel = event.getChannel();
                if (channel instanceof TextChannel) {
                    if (Text.comparablyContains(channel.getName(), "general")) {
                        checkForDiscordLink(event.getAuthor().getIdLong(), message, event.getMessage());
                    }

                    if (Text.comparablyContains(channel.getName(), REWARD_CHANNELS.toArray())) {
                        Main.async(() -> checkForRewards(event.getAuthor().getIdLong(), message, event.getMessage()));
                    }

                    moderationCheck((TextChannel) channel, event.getMessage());
                }
            }
        }
    }

    public static void moderationCheck(TextChannel channel, Message message) {
        Member member = getGuild().getMember(message.getAuthor());
        if (member != null) {
            for (Role role : member.getRoles()) {
                if (Text.comparablyContains(role.getName(), "staff")) {
                    return;
                }
            }
        }
        String rawContent = message.getContentRaw();
        if (!Text.comparablyContains(channel.getName(), "media")) {
            if (PlayerUtilities.containsWebsiteOrData(rawContent)) {
                message.delete().queue();
            }
        }
        if (rawContent.contains(".gg")) {
            message.delete().queue();
        }
    }

    public static long getServerID() {
        return RelayUtils.testing ? TESTING_SERVER_ID : SERVER_ID;
    }

    private static void checkForDiscordLink(long idLong, String message, Message originalMessage) {
        UUID player = CODE_TO_PLAYER.remove(message);

        if (player != null) {
            SmartPlayer smartPlayer = SmartPlayer.getOrCreate(player);
            smartPlayer.setDiscordId(idLong);
            originalMessage.delete().queue();
            ID_TO_UUID.put(idLong, smartPlayer.getUUID());
            try {
                PrivateChannel privateChannel = originalMessage.getAuthor().openPrivateChannel().submit().get();
                if (originalMessage.getAuthor().hasPrivateChannel()) {
                    privateChannel.sendMessage("You have successfully linked your Discord account!\n" + smartPlayer.getName()).queue();
                }
            } catch (InterruptedException | ExecutionException ignored) {

            }
            smartPlayer.sendSuccessMessage("You have successfully linked your Discord account: %s%s", ChatColor.GOLD, originalMessage.getAuthor().getName());
        }
    }

    private static void checkForRewards(long idLong, String message, Message originalMessage) {
        SmartPlayer smartPlayer = getSmartPlayer(idLong);
        if (smartPlayer == null) {
            return;
        }

        smartPlayer.onDiscordMessage(message);
    }

    public static final Map<Long, UUID> ID_TO_UUID = new HashMap<>();

    public static SmartPlayer getSmartPlayer(long idLong) {
        UUID uuid;

        uuid = ID_TO_UUID.get(idLong);

        if (uuid == null) {
            try {

                ResultSet resultSet = SmartDatabase.getFrom(SmartDatabase.PROFILE_TABLE_NAME, ProfileColumn.DISCORD_ID.getName(), String.valueOf(idLong), ProfileColumn.ID.getName()).get(5, TimeUnit.SECONDS);
                if (resultSet.first()) {
                    uuid = UUID.fromString(resultSet.getString(ProfileColumn.ID.getName()));
                    ID_TO_UUID.put(idLong, uuid);
                }
            } catch (Exception e) {
                ID_TO_UUID.put(idLong, null);
            }
        }

        return SmartPlayer.getOrCreate(uuid);
    }

    private static void onReady(ReadyEvent event) {
        if (getGuild() == null) {
            error("Could not find guild!!");
        } else {
            getGuild().updateCommands().addCommands(Commands.slash("link", "Link your Discord account by entering a code.")
                    .addOption(OptionType.STRING, "code", "Code given by /discord link in-game.").setDefaultPermissions(DefaultMemberPermissions.ENABLED)).queue();
        }
    }

    public static Guild getGuild() {
        return BOT.getGuildById(RelayUtils.testing ? TESTING_SERVER_ID : SERVER_ID);
    }

    public static void updatePlayerCount(boolean playerCount) {
        if (BOT == null) return;
        if (playerCount) {
            BOT.getPresence().setActivity(Activity.watching(Main.getOnlineCount() + " players online"));
        } else {
            BOT.getPresence().setActivity(Activity.playing("relaymc.net"));
        }
    }

    public static void startup() {

    }

    public static void sendMessage(String channel, String format, Object... data) {
        if (getGuild() == null) {
            error("Tried to send message to channel, guild is null");
            return;
        }

        String message = RelayUtils.format(format, data);

        GuildChannel ch = getGuildChannel(channel);

        if (ch == null) {
            error("No channel by the name of %s", channel);
            return;
        }

        if (ch instanceof TextChannel) {
            ((TextChannel) ch).sendMessage(message).queue();
        } else {
            error("Couldn't cast %s to TextChannel", channel);
        }
    }

    public static GuildChannel getGuildChannel(String name) {
        return Miscellaneous.match(getGuild().getChannels(), guildChannel -> guildChannel.getName().equals(name));
    }

    public static TextChannel getTextChannel(String name) {
        return (TextChannel) getGuildChannel(name);
    }

    public static void info(String format, Object... data) {
        Main.info("BOT > " + format, data);
    }

    public static void error(String format, Object... data) {
        Main.error("BOT > " + format, data);
    }

    public static final Map<Report, String> REPORT_TO_MESSAGE = new HashMap<>();
    public static final Map<Report, EmbedBuilder> REPORT_TO_CACHED = new HashMap<>();

    public static String getPlayerFaceURL(UUID uuid) {
        return "https://crafatar.com/avatars/" + uuid;
    }

    public static void report(Report report) {
        if (report.getReportType() == ReportType.BUG) {
            EmbedBuilder embed = new EmbedBuilder().setTitle("ID: " + report.getId()).setDescription(getDeveloperMention() + " Bug Report").setColor(Color.GREEN);
            embed.addBlankField(false);
            embed.addField("Reason", report.getReportType().toString().toUpperCase(), true);
            embed.addField("Reporter", report.getReporter().getName(), true);
            embed.addField("Server", report.getServer().getName(), true);
            embed.addField("Message", report.getReason(), true);
            embed.setThumbnail(getPlayerFaceURL(report.getReporter().getUUID()));

            MessageEmbed messageEmbed = embed.build();

            CompletableFuture<Message> completableFuture = getTextChannel("devalerts").sendMessageEmbeds(messageEmbed).submit();
            alertDevs("New bug report!");
        } else {
            EmbedBuilder embed = new EmbedBuilder().setTitle("ID: " + report.getId()).setDescription(getStaffMention() + " Player: " + report.getReported().getName()).setColor(Color.GREEN.darker());
            embed.addBlankField(false);
            embed.addField("Reason", report.getReportType().toString().toUpperCase(), true);
            embed.addField("Reporter", report.getReporter().getName(), true);
            embed.addField("Server", report.getServer().getName(), true);
            embed.addField("Message", report.hasReason() ? report.getReason() : "None", true);
            embed.setThumbnail(getPlayerFaceURL(report.getReported().getUUID()));
            alertStaff("reports", "New report!");
            try {
                MessageEmbed messageEmbed = embed.build();
                REPORT_TO_CACHED.put(report, embed);
                CompletableFuture<Message> completableFuture = getTextChannel("reports").sendMessageEmbeds(messageEmbed).submit();
                String id = completableFuture.get().getId();
                REPORT_TO_MESSAGE.put(report, id);
            } catch (ExecutionException | InterruptedException ignored) {

            }
        }
    }

    public static void onReportManaged(Report report) {
        EmbedBuilder embedBuilder = REPORT_TO_CACHED.get(report);

        embedBuilder.addField("Dismisser", report.hasDismisser() ? report.getDismisser().getName() : "Console", true);
        embedBuilder.addField("Accepted", report.isAccepted() ? "YES" : "NO", true);
        embedBuilder.addField("Tokens Rewarded: ", String.valueOf(report.getTokensRewarded()), true);
        embedBuilder.addField("Message", report.hasDismissMessage() ? report.getDismissMessage() : "None", true);

        getTextChannel("reports").editMessageEmbedsById(REPORT_TO_MESSAGE.get(report), embedBuilder.build()).queue();
    }

    public static final Map<Punishment, String> PUNISHMENT_TO_MESSAGE = new HashMap<>();
    public static final Map<Punishment, EmbedBuilder> PUNISHMENT_TO_CACHED = new HashMap<>();

    public static final String BOT_PFP = "https://media.discordapp.net/attachments/483743463838318613/1141124785187868693/r.png";
    public static final String SERVER_ICON = "https://media.discordapp.net/attachments/1060535096509206538/1137921396664696922/Relay_Logo_Square.png?width=604&height=604";

    public static void punish(Punishment punishment) {
        EmbedBuilder embed = new EmbedBuilder().setTitle("ID: " + punishment.getID()).setDescription("Player: " + SmartPlayer.getOrCreate(punishment.getPunished()).getName())
                .setColor(ComponentBuilder.GREEN.darker());
        embed.addBlankField(false);
        embed.addField("Type", punishment.getType().toString().toUpperCase(), true);
        embed.addField("Punisher", punishment.wasPunishedByConsole() ? "Console" : SmartPlayer.getOrCreate(punishment.getPunisher()).getName(), true);
        embed.addField("Punishment ID", punishment.getBanID(), true);
        embed.addField("Duration", punishment.isIndefinite() ? "Permanent" : Text.getShortTextualFormattedTime(punishment.getDuration()), true);
        if (!punishment.isIndefinite()) {
            embed.addField("Expires", Text.getDateTimeString(punishment.getTime() + punishment.getDuration()), true);

        }
        embed.addField("Reason", punishment.hasReason() ? punishment.getReason() : "None", true);
        embed.setThumbnail(getPlayerFaceURL(punishment.getPunished()));
        try {
            PUNISHMENT_TO_CACHED.put(punishment, embed);
            CompletableFuture<Message> completableFuture = getTextChannel("punishments").sendMessageEmbeds(embed.build()).submit();
            String id = completableFuture.get().getId();
            PUNISHMENT_TO_MESSAGE.put(punishment, id);
        } catch (ExecutionException | InterruptedException ignored) {

        }
    }

    public static void shutdown() {
        for (Punishment punishment : PUNISHMENT_TO_MESSAGE.keySet()) {
            EmbedBuilder embedBuilder = PUNISHMENT_TO_CACHED.get(punishment);
            if (!punishment.isActive()) {
                embedBuilder.setColor(Color.RED);
                embedBuilder.addField("Revoked", Text.getDateString(punishment.getRevokeTime()) + " " + Text.getTimeString(punishment.getRevokeTime()), true);
                embedBuilder.addField("Revoker", punishment.wasRevokedByConsole() ? "Console" : SmartPlayer.getOrCreate(punishment.getRevoker()).getName(), true);
            } else {
                embedBuilder.setColor(Color.YELLOW);
                embedBuilder.addField("Status", "NOT SYNCED TO SERVER", false);
            }
            getTextChannel("punishments").editMessageEmbedsById(PUNISHMENT_TO_MESSAGE.get(punishment), embedBuilder.build()).queue();
        }

        for (Report report : REPORT_TO_MESSAGE.keySet()) {
            EmbedBuilder embedBuilder = REPORT_TO_CACHED.get(report);
            if (!(report.isAccepted() || report.isDismissed())) {
                embedBuilder.setColor(Color.YELLOW);
            }

            embedBuilder.addField("Status", "NO LONGER AVAILABLE", false);

            getTextChannel("reports").editMessageEmbedsById(REPORT_TO_MESSAGE.get(report), embedBuilder.build()).queue();
        }

        BOT.shutdown();
    }

    public static final List<Long> NULL_SERVER_ISSUES = new ArrayList<>();

    public static void onNullServer() {
        long currentTime = System.currentTimeMillis();
        NULL_SERVER_ISSUES.add(currentTime);
        Main.schedule(() -> NULL_SERVER_ISSUES.remove(currentTime), 60);
    }

    public static final Map<Long, Exception> SCHEDULED_TASK_ERRORS = new HashMap<>();

    public static void onScheduledTaskError(Exception e) {
        long currentTime = System.currentTimeMillis();
        SCHEDULED_TASK_ERRORS.put(currentTime, e);
        Main.schedule(() -> SCHEDULED_TASK_ERRORS.remove(currentTime), 60);

        if (SCHEDULED_TASK_ERRORS.size() > 10) {
            Exception exception = Miscellaneous.getLastEntry(SCHEDULED_TASK_ERRORS.values());
            if (exception == null) {
                return;
            }
            alertDevs("%d Scheduled tasks errors! (Latest: %s \n%s)", SCHEDULED_TASK_ERRORS.size(), exception.getMessage(), Text.separateBy(Miscellaneous.to(exception.getStackTrace(), 5), "\n"));
        }
    }

    private static final String DEVELOPER_MENTION = "<@&793213378654371853>";
    private static final String TESTING_DEVELOPER_MENTION = "<@&1141110691760177262>";

    public static String getDeveloperMention() {
        if (RelayUtils.testing) {
            return TESTING_DEVELOPER_MENTION;
        }

        return DEVELOPER_MENTION;
    }

    public static void alertDevs(String format, Object... data) {
        sendMessage("devalerts", getDeveloperMention() + " " + RelayUtils.format(format, data));
    }

    private static final String STAFF_MENTION = "<@&793247946064920608>";
    private static final String TESTING_STAFF_MENTION = "<@&1141123648858619998>";

    public static String getStaffMention() {
        if (RelayUtils.testing) {
            return TESTING_STAFF_MENTION;
        }

        return STAFF_MENTION;
    }

    public static void alertStaff(String channel, String format, Object... data) {
        sendMessage(channel, getStaffMention() + " " + RelayUtils.format(format, data));
    }

    public static void onTakenCredits(SmartPlayer smartPlayer, long credits) {
        sendMessage("purchases", "%s canceled %d credits.", smartPlayer.getName(), credits);
    }

    public static void onTakenGroup(SmartPlayer smartPlayer, Group group) {
        sendMessage("purchases", "%s canceled %s purchase.", smartPlayer.getName(), group.getGroupName());
    }

    public static void onGivenGroup(SmartPlayer smartPlayer, Group group) {
        sendMessage("purchases", "%s purchased %s.", smartPlayer.getName(), group.getGroupName());
    }

    public static void onGivenTokens(SmartPlayer smartPlayer, long credits) {
        sendMessage("purchases", "%s purchased %d credits.", smartPlayer.getName(), credits);
    }

    public static final Map<String, UUID> CODE_TO_PLAYER = new HashMap<>();

    public static void onDiscordCodeGenerated(SmartPlayer smartPlayer, String code) {
        CODE_TO_PLAYER.put(code, smartPlayer.getUUID());
    }

    public static void onPlayerDataRetrieved(SmartPlayer smartPlayer) {
        if (smartPlayer.hasDiscordLinked()) {
            ID_TO_UUID.put(smartPlayer.getDiscordId(), smartPlayer.getUUID());
        }
    }
}