sx.blah.discord.SpoofBot.java Source code

Java tutorial

Introduction

Here is the source code for sx.blah.discord.SpoofBot.java

Source

/*
 *     This file is part of Discord4J.
 *
 *     Discord4J is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Discord4J is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with Discord4J.  If not, see <http://www.gnu.org/licenses/>.
 */

package sx.blah.discord;

import org.apache.commons.lang3.ClassUtils;
import sx.blah.discord.api.ClientBuilder;
import sx.blah.discord.api.IDiscordClient;
import sx.blah.discord.api.events.IListener;
import sx.blah.discord.handle.impl.events.ReadyEvent;
import sx.blah.discord.handle.obj.*;
import sx.blah.discord.util.DiscordException;
import sx.blah.discord.util.MissingPermissionsException;
import sx.blah.discord.util.RateLimitException;

import java.awt.Color;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayDeque;
import java.util.EnumSet;
import java.util.Random;

/**
 * Used to simulate a server.
 */
public class SpoofBot {

    private static final Random rng = new Random();
    private IChannel channel;
    private Spoofs lastSpoof;
    private Object lastSpoofData;
    private ArrayDeque<Spoofs> enqueued = new ArrayDeque<>();
    private volatile long lastTime;
    private volatile long timer;
    private final IDiscordClient other;
    private final IDiscordClient client;

    public SpoofBot(IDiscordClient other, String token, long channelID) throws Exception {
        this.other = other;
        client = new ClientBuilder().withToken(token).login();
        client.getDispatcher().registerListener(new IListener<ReadyEvent>() {

            @Override
            public void handle(ReadyEvent event) {
                try {
                    channel = client.getChannelByID(channelID);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException("Spoofing failed!");
                }
                timer = 1;
                lastTime = System.currentTimeMillis() - timer;
                new Thread(() -> {
                    while (true) {
                        if (lastTime <= System.currentTimeMillis() - timer) {
                            //Time for the next spoof
                            timer = getRandTimer();
                            lastTime = System.currentTimeMillis();
                            synchronized (client) {
                                if (!enqueued.isEmpty()) {
                                    Spoofs toSpoof = enqueued.pop();
                                    if (toSpoof.getDependent() == null
                                            || toSpoof.getDependent().equals(lastSpoof)) {
                                        //Handle each spoof
                                        switch (toSpoof) {
                                        case MESSAGE:
                                            channel.toggleTypingStatus();
                                            try {
                                                lastSpoofData = channel.sendMessage(
                                                        (rng.nextInt(10) == 9 ? other.getOurUser().mention() + " "
                                                                : "") + getRandMessage());
                                            } catch (MissingPermissionsException | RateLimitException
                                                    | DiscordException e) {
                                                e.printStackTrace();
                                            }
                                            break;

                                        case MESSAGE_EDIT:
                                            try {
                                                ((IMessage) lastSpoofData).edit(getRandMessage());
                                            } catch (MissingPermissionsException | RateLimitException
                                                    | DiscordException e) {
                                                e.printStackTrace();
                                            }
                                            break;

                                        case TYPING_TOGGLE:
                                            channel.toggleTypingStatus();
                                            break;

                                        case GAME:
                                            client.changePresence(StatusType.ONLINE, ActivityType.PLAYING,
                                                    rng.nextBoolean() ? getRandString() : null);
                                            break;

                                        case PRESENCE:
                                            break;

                                        case MESSAGE_DELETE:
                                            try {
                                                ((IMessage) lastSpoofData).delete();
                                            } catch (MissingPermissionsException | RateLimitException
                                                    | DiscordException e) {
                                                e.printStackTrace();
                                            }
                                            break;

                                        case INVITE:
                                            IInvite invite = null;
                                            try {
                                                invite = client.getGuilds().get(0).getChannels()
                                                        .get(rng.nextInt(
                                                                client.getGuilds().get(0).getChannels().size()))
                                                        .createInvite(18000, 1, false, false);
                                            } catch (MissingPermissionsException | RateLimitException
                                                    | DiscordException e) {
                                                e.printStackTrace();
                                            }
                                            if (invite.getCode() != null) {
                                                try {
                                                    channel.sendMessage("https://discord.gg/" + invite.getCode());
                                                } catch (MissingPermissionsException | RateLimitException
                                                        | DiscordException e) {
                                                    e.printStackTrace();
                                                }
                                            }
                                            lastSpoofData = invite;
                                            break;

                                        case CHANNEL_CREATE_AND_DELETE:
                                            try {
                                                final IChannel newChannel = channel.getGuild()
                                                        .createChannel(getRandString());
                                                final long deletionTimer = getRandTimer()
                                                        + System.currentTimeMillis();
                                                new Thread(() -> {
                                                    while (deletionTimer > System.currentTimeMillis()) {
                                                    }
                                                    try {
                                                        newChannel.delete();
                                                    } catch (MissingPermissionsException | RateLimitException
                                                            | DiscordException e) {
                                                        e.printStackTrace();
                                                    }
                                                }).start();
                                            } catch (DiscordException | MissingPermissionsException
                                                    | RateLimitException e) {
                                                e.printStackTrace();
                                            }
                                            break;

                                        case CHANNEL_EDIT:
                                            try {
                                                channel.changeName(getRandString());
                                                channel.changeTopic(getRandSentence());
                                            } catch (DiscordException | MissingPermissionsException
                                                    | RateLimitException e) {
                                                e.printStackTrace();
                                            }
                                            break;

                                        case ROLE_CREATE_EDIT_AND_DELETE:
                                            try {
                                                final IRole role = channel.getGuild().createRole();
                                                role.changeColor(new Color(rng.nextInt(255), rng.nextInt(255),
                                                        rng.nextInt(255)));
                                                role.changeName(getRandString());
                                                role.changePermissions(EnumSet.allOf(Permissions.class));
                                                final long deletionTimer = getRandTimer()
                                                        + System.currentTimeMillis();
                                                new Thread(() -> {
                                                    while (deletionTimer > System.currentTimeMillis()) {
                                                    }
                                                    try {
                                                        role.delete();
                                                    } catch (MissingPermissionsException | RateLimitException
                                                            | DiscordException e) {
                                                        e.printStackTrace();
                                                    }
                                                }).start();
                                            } catch (MissingPermissionsException | RateLimitException
                                                    | DiscordException e) {
                                                e.printStackTrace();
                                            }
                                            break;
                                        }
                                        lastSpoof = toSpoof;
                                    } else {
                                        //Dependent missing? Not a problem, we'll just do the dependent instead
                                        enqueued.addFirst(toSpoof);
                                        enqueued.addFirst(toSpoof.getDependent());
                                        timer = 0;
                                    }
                                } else {
                                    //No spoofs queued, better randomize them
                                    for (int i = 0; i < 10; i++)
                                        enqueued.add(
                                                Spoofs.values()[rng.nextInt(EnumSet.allOf(Spoofs.class).size())]);
                                }
                            }
                        }
                    }
                }).start();
            }
        });
    }

    /**
     * Generates a random message.
     *
     * @return The random message.
     */
    public static String getRandMessage() {
        int sentenceCount = rng.nextInt(3) + 1; //The message will have 1-3 sentences.
        String[] sentences = new String[sentenceCount];
        for (int i = 0; i < sentences.length; i++) {
            sentences[i] = getRandSentence();
        }

        String message = "";
        for (String sentence : sentences) {
            message += sentence + getRandEndingPunctuation() + " ";
        }
        return message;
    }

    /**
     * Generates a random sentence.
     *
     * @return The random sentence.
     */
    public static String getRandSentence() {
        int wordCount = rng.nextInt(10) + 1;
        String[] words = new String[wordCount];
        for (int i = 0; i < wordCount; i++)
            words[i] = rng.nextInt(9) == 8 ? String.valueOf(rng.nextInt(100)) : getRandString(); //Gets either a random number or a random string

        String message = "";
        for (String word : words) {
            message += word + getRandCharacter("      :,;".toCharArray()); //randomizes inline punctuation, weighs spaces more heavily
            if (message.charAt(message.length() - 1) != ' ')
                message += " ";
        }
        message = message.substring(0, message.length() - 2);

        if (!isNumber(String.valueOf(message.charAt(0)))) {
            message = Character.toUpperCase(message.charAt(0)) + message.substring(1);
        }
        return message;
    }

    /**
     * Checks whether a string is a number.
     *
     * @param s The string to check.
     * @return True if the string is a number, false if otherwise.
     */
    public static boolean isNumber(String s) {
        try {
            Integer.valueOf(s);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * Gets a random ending punctuation.
     *
     * @return The random character.
     */
    public static Character getRandEndingPunctuation() {
        return getRandCharacter("?!....".toCharArray()); //Weighted towards periods
    }

    /**
     * Randomizes the time to wait until the next spoof action.
     *
     * @return The time in milliseconds.
     */
    public static long getRandTimer() {
        return 1000L + (long) (rng.nextDouble() * (3000L - 1000L)); //Timer between 1 to 3 seconds
    }

    /**
     * Gets a random number based on the number type provided.
     *
     * @param clazz The number type class.
     * @return The randomized number.
     */
    public static Number getRandNumber(Class<? extends Number> clazz) {
        Long bound;
        try {
            Field max_value = clazz.getDeclaredField("MAX_VALUE");
            bound = (Long) max_value.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            bound = (long) Byte.MAX_VALUE; //Supports all possible number types
        }
        return rng.nextDouble() * bound;
    }

    /**
     * Gets a random boolean.
     *
     * @return The random boolean.
     */
    public static Boolean getRandBoolean() {
        return rng.nextBoolean();
    }

    /**
     * Gets a randomized character from the specificed charset.
     *
     * @param charSet The character set.
     * @return The randomized character.
     */
    public static Character getRandCharacter(char[] charSet) {
        return charSet[rng.nextInt(charSet.length)];
    }

    /**
     * Gets a random alphabetical character.
     *
     * @return The random character.
     */
    public static Character getRandCharacter() {
        return getRandCharacter("abcdefghijklmnopqrstuvwxyz".toCharArray());
    }

    /**
     * Randomizes a string.
     *
     * @return The random string.
     */
    public static String getRandString() {
        char[] characters = new char[rng.nextInt(16) + 3]; //Uses 3-16 characters
        for (int i = 0; i < characters.length; i++) {
            characters[i] = getRandCharacter();
        }
        return new String(characters);
    }

    /**
     * Checks if the class is supported to be randomized without recursion.
     *
     * @param clazz The class to check.
     * @return True if supported, false if otherwise.
     */
    public static boolean canBeRandomized(Class clazz) {
        return ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.equals(String.class)
                || clazz.equals(IDiscordClient.class);
    }

    /**
     * Randomly constructs an object.
     *
     * @param client The discord client.
     * @param clazz The class to construct.
     * @param <T> The type of object to construct.
     * @return The constructed object (or null if not possible).
     *
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws InstantiationException
     */
    public static <T> T randomizeObject(IDiscordClient client, Class<T> clazz)
            throws IllegalAccessException, InvocationTargetException, InstantiationException {
        if (canBeRandomized(clazz)) {
            if (String.class.isAssignableFrom(clazz))
                return (T) getRandString();
            else if (Character.class.isAssignableFrom(clazz))
                return (T) getRandCharacter();
            else if (Boolean.class.isAssignableFrom(clazz))
                return (T) getRandBoolean();
            else if (Number.class.isAssignableFrom(clazz))
                return (T) getRandNumber((Class<? extends Number>) clazz);
            else if (Void.class.isAssignableFrom(clazz))
                return null;
            else if (IDiscordClient.class.isAssignableFrom(clazz))
                return (T) client;

        } else {
            outer: for (Constructor constructor : clazz.getConstructors()) {
                Object[] parameters = new Object[constructor.getParameterCount()];
                for (Class<?> param : constructor.getParameterTypes()) {
                    if (!canBeRandomized(param))
                        continue outer;
                }
                if (parameters.length > 0) {
                    for (int i = 0; i < parameters.length; i++) {
                        parameters[i] = randomizeObject(client, constructor.getParameterTypes()[i]);
                    }
                    return (T) constructor.newInstance(parameters);
                } else {
                    return (T) constructor.newInstance();
                }
            }
        }
        return null;
    }

    /**
     * Adds specific spoofs
     *
     * @param toSpoof The specific spoof to add
     */
    public void spoof(Spoofs toSpoof) {
        enqueued.addLast(toSpoof);
    }

    /**
     * Represents the kind of spoofing this bot is capable of.
     */
    public enum Spoofs {
        MESSAGE("TYPING_TOGGLE"), MESSAGE_EDIT("MESSAGE"), TYPING_TOGGLE(null), GAME(null), PRESENCE(
                null), MESSAGE_DELETE("MESSAGE"), INVITE(null), CHANNEL_CREATE_AND_DELETE(
                        null), CHANNEL_EDIT(null), ROLE_CREATE_EDIT_AND_DELETE(null);

        String dependsOn;

        Spoofs(String dependsOn) {
            this.dependsOn = dependsOn;
        }

        /**
         * Gets the spoof required for this spoof to run.
         *
         * @return The dependent spoof.
         */
        public Spoofs getDependent() {
            return dependsOn == null ? null : Spoofs.valueOf(dependsOn);
        }
    }
}