Thread: Discord API

Page 1 of 2 12 LastLast
Results 1 to 10 of 20
  1. #1 Discord API 
    Software Developer

    Tyrant's Avatar
    Join Date
    Jul 2013
    Age
    24
    Posts
    1,562
    Thanks given
    678
    Thanks received
    423
    Rep Power
    1060
    Seems like I don't have much time to update this. The only thing really left here is to use a proper async operations for the bot actions (so its not done through the game thread, and does not block it or interrupt with it.)
    Also actions needs to be chained properly. (isn't a must but useful). Consider this a base.

    Spoiler for Deprecated:

    Haye.

    I've recently came across "Discord4J" and decided to implement and design it in a RSPS.
    This is just a base, and I'm looking to expand this and redesign some parts of it, however, I believe it can be used
    and that it should be flexible enough so you can work this further out.

    Notice: This also comes with an uncompleted command system (I haven't really figured if I want to really advance the usage of commands, so atm it's quite basic)


    First off, get your libraries from: https://github.com/austinv11/Discord4J

    (Gradle / Maven is recommended)

    Spoiler for Reflections & SLF4J:

    gradle:
    Code:
        //reflection
        compile 'org.reflections:reflections:0.9.11'
        
        //slf4j
        compile 'org.slf4j:slf4j-api:1.7.25'
    
        //discord4j
        compile 'com.github.austinv11:Discord4j:2.10.0'


    Spoiler for Create your Discord APP -> Bot:


    Refer to: https://discordapp.com/developers/applications/me

    New app
    Attached image

    Then configure

    Attached image

    You'll get to this page:

    Attached image

    On that page, scroll down and press
    'Create a Bot user' -> Yes do it..

    Then reveal and SAVE THE TOKEN (You can always just regenerate but cmon)
    Attached image

    when you're done, save changes. and then on the same page click

    Attached image

    You will be then promoted to this page:
    Attached image
    (Set permissions to w.e you want)
    YOU WILL SEE A LINK. CLICK THAT FUCKING LINK AND AUTHORIZE

    Yer done.


    Spoiler for Grab Discord Token:

    See the first step. (Spoiler above)
    [The token that you were told to save)
    Attached image


    Spoiler for Get channel ID:

    On your discord desktop app:

    Go to settings
    Attached image

    Then go to Appereance and enable Developer mode
    Attached image

    On any discord server (but obviously, do it on the one you linked the bot to)
    Right click a channel (In this case, the one you want to be the "default" channel that the bot will speak on)
    and then "Copy Id"
    Attached image
    SAVE THE ID, AND PUT IT ON THE CODE AT "MAIN_COMMUNITY_CHANNEL_ID = YOUR_CHANNEL_ID" @ DiscordAPI.java


    Spoiler for Creating commands:

    We're using Reflections API which allows us to create commands without manually registering them.
    Simply follow the way "TestDiscordCommand" goes like and figure your way to make some commands [The settings annotation is a must]

    NOTE: You need to replace the "COMMANDS_IMPL_DIRECTORY" @ DiscordCommandFactory to the directory where you IMPLEMENT THE COMMANDS.
    I.E "com.runeavion.core.network.discord.commands.i mpl" (On 'impl' folder we have the commands, but it will also count for any subpackage within 'impl' package [put into simple words: impl/staff will also register])


    ---

    Spoiler for Discord API:

    Code:
    package com.runeavion.core.network.discord;
    
    import com.runeavion.util.ThreadUtil;
    import sx.blah.discord.handle.obj.IChannel;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    /**
     * Discord API by Discord4J. Implemented and designed by Tyrant
     *
     * @author Tyrant
     */
    public class DiscordAPI {
    
        /**
         * The responsible communicator for communication between this API and the discord client
         */
        public static final DiscordCommunicator DISCORD_COMMUNICATOR = new DiscordCommunicator();
    
        /**
         * Indicates if we have reached a connection to the discord client.
         */
        public static final AtomicBoolean CONNECTED = new AtomicBoolean();
    
        /**
         * The main community channel for default communication
         */
        public static final long MAIN_COMMUNITY_CHANNEL_ID = YOUR_CHANNEL_ID;
    
        /**
         * The thread that this API will be depending on.
         * Please notice that this isn't what the dispatchers will run on, since the API
         * provides us with its own executor. This is used for the starting up of this API (since we block)
         * and for the future, we will convert into a blocking-queue supportive and have a proper queue
         * of registered events
         */
        private static final Executor DISCORD_EXECUTOR =
                Executors.newSingleThreadScheduledExecutor(ThreadUtil.create("DiscordExecutorThread"));
    
        /**
         * The time it takes out before we timeout the connection with the discord server
         */
        private static final long CONNECTION_TIMEOUT = TimeUnit.MINUTES.toMillis(5);
    
        /**
         * Start the discord API
         */
        public static void start() {
            DISCORD_EXECUTOR.execute(() -> {
                try {
                    long start = System.nanoTime();
    
                    //attempt to login
                    DISCORD_COMMUNICATOR.login();
    
                    //block until we are connected. we're doing this concurrently so we're fine with locking
                    while (!DISCORD_COMMUNICATOR.getClient().isReady()) {
                        //TODO add some sort of shutdown if connection failed after a while
                        if (System.nanoTime() - start >= CONNECTION_TIMEOUT) {
                            throw new IllegalStateException("Discord API took too long to connect.");
                        }
                    }
                } finally {
                    DISCORD_COMMUNICATOR.dispatch();
                }
            });
        }
    
        //static references
    
        /**
         * Message to default channel
         */
        public static void message(String message) {
            message(DISCORD_COMMUNICATOR.getDefaultChannel(), message);
        }
    
        /**
         * Send message to a channel
         */
        public static void message(IChannel channel, String message) {
            queue(args -> channel.sendMessage(message));
        }
    
        /**
         * TODO: Create a queueing system
         */
        public static void queue(DiscordEvent event) {
            if (!CONNECTED.get()) {
                throw new IllegalStateException("Discord API isn't online");
            }
            DISCORD_EXECUTOR.execute(event::action);
        }
    
    }
    Code:
    package com.runeavion.core.network.discord;
    
    import com.runeavion.Config;
    import com.runeavion.core.network.discord.commands.DiscordCommandDispatcher;
    import com.runeavion.core.network.discord.commands.DiscordCommandFactory;
    import sx.blah.discord.api.ClientBuilder;
    import sx.blah.discord.api.IDiscordClient;
    import sx.blah.discord.handle.obj.IChannel;
    
    /**
     * The client whom acts as a pipeline of communication between the discord client to this API
     *
     * @author Tyrant
     * @date 3/6/2018
     */
    public class DiscordCommunicator {
    
        /**
         * The client builder.
         */
        private final ClientBuilder builder;
    
        /**
         * The discord client.
         */
        private final IDiscordClient client;
    
        /**
         * The default channel to communicate on
         */
        private IChannel defaultChannel;
    
        public DiscordCommunicator() {
            builder = new ClientBuilder().withToken(YOUR_DISCORD_TOKEN);
            client = builder.build();
        }
    
        public DiscordCommunicator login() {
            if (client.isLoggedIn()) {
                throw new IllegalStateException("Discord API already logged in.");
            }
            client.login();
            return this;
        }
    
        public IChannel channel(long id) {
            for (IChannel channel : client.getChannels()) {
                if (channel != null && channel.getLongID() == id)
                    return channel;
            }
            return null;
        }
    
        /**
         * Dispatch every event when this is ready
         */
        public DiscordCommunicator dispatch() {
            try {
                defaultChannel = channel(DiscordAPI.MAIN_COMMUNITY_CHANNEL_ID);
                //TODO: reflection find any dispatcher and register
                client.getDispatcher().registerListener(new DiscordCommandDispatcher());
            } finally {
                DiscordCommandFactory.init();
                DiscordAPI.CONNECTED.set(true);
            }
            return this;
        }
    
        public IDiscordClient getClient() {
            return client;
        }
    
        public ClientBuilder getBuilder() {
            return builder;
        }
    
        public IChannel getDefaultChannel() {
            return defaultChannel;
        }
    }
    Code:
    package com.runeavion.core.network.discord;
    
    /**
     * A dispatcher that can be registered
     *
     * @author Tyrant
     * @date 3/7/2018
     */
    public interface DiscordDispatcher {
    }
    Code:
    package com.runeavion.core.network.discord;
    
    /**
     * An event that happens through the discord client
     *
     * @author Tyrant
     * @date 3/6/2018
     */
    public interface DiscordEvent {
    
        /**
         * Do action with potentially given arguments
         */
        void action(Object... args);
    }
    commands package

    Code:
    package com.runeavion.core.network.discord.commands;
    
    import com.runeavion.core.network.discord.DiscordEvent;
    
    /**
     * A discord command event
     *
     * @author Tyrant
     */
    public interface DiscordCommand extends DiscordEvent {
    
    }
    Code:
    package com.runeavion.core.network.discord.commands;
    
    import com.runeavion.core.network.discord.DiscordAPI;
    import com.runeavion.core.network.discord.DiscordDispatcher;
    import sx.blah.discord.api.events.EventSubscriber;
    import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
    import sx.blah.discord.handle.obj.IChannel;
    import sx.blah.discord.handle.obj.IMessage;
    import sx.blah.discord.handle.obj.IUser;
    import sx.blah.discord.util.DiscordException;
    import sx.blah.discord.util.MissingPermissionsException;
    
    /**
     * Discord command dispatcher
     *
     * @author Tyrant
     * @date 3/7/2018
     */
    public class DiscordCommandDispatcher implements DiscordDispatcher {
    
        @EventSubscriber
        public void OnMesageEvent(MessageReceivedEvent event) throws DiscordException, MissingPermissionsException {
            IMessage message = event.getMessage();
            long channelId = event.getChannel().getLongID();
            IUser user = event.getAuthor();
            String input = message.getContent();
    
            //get the channel we got this command from or else the default channel
            IChannel channel = DiscordAPI.DISCORD_COMMUNICATOR.channel(channelId);
            if (channel == null) {
                channel = DiscordAPI.DISCORD_COMMUNICATOR.getDefaultChannel();
            }
    
            if (input.startsWith("::")) {
                input = input.substring(2);//start after ::
                DiscordCommandFactory.handleCommand(channel, user, input);
            }
        }
    }
    Code:
    package com.runeavion.core.network.discord.commands;
    
    
    import com.runeavion.core.network.discord.DiscordAPI;
    import org.reflections.Reflections;
    import sx.blah.discord.handle.obj.IChannel;
    import sx.blah.discord.handle.obj.IRole;
    import sx.blah.discord.handle.obj.IUser;
    
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    import java.util.logging.Logger;
    
    /**
     * The command system
     *
     * @author Tyrant
     * @date 3/6/2018
     */
    public class DiscordCommandFactory {
    
        private static final Logger LOGGER = Logger.getLogger(DiscordCommand.class.getName());
    
        /**
         * The map that stores every command where <code>key</code> represents the name of the command
         * and the <code>value</code> represents the functional command.
         */
        private static final Map<String, DiscordCommand> COMMANDS = new HashMap<>();
    
        /**
         * The directory for the package-parent of all commands
         */
        private static final String COMMANDS_IMPL_DIRECTORY = "com.runeavion.core.network.discord.commands.impl";
    
        /**
         * The message you receive when an error occurred while executing the command (Unless {@link DiscordCommandSettings#syntaxPrefix()} is specified)
         */
        private static final String DEFAULT_SYNTAX_ERROR_MESSAGE = "Error executing command. Input: {INPUT}";
    
        /**
         * The initial method to load commands, loads them using reflections, hence,
         * must ensure classes being the same instance of {@link DiscordCommand}
         */
        public static void init() {
            COMMANDS.clear();
    
            Set<Class<?>> annotated = new Reflections(COMMANDS_IMPL_DIRECTORY).getTypesAnnotatedWith(DiscordCommandSettings.class);
            for (Class<?> aClass : annotated) {
                Arrays.asList(aClass.getAnnotation(DiscordCommandSettings.class).accessInputs()).forEach($it -> {
                    try {
                        COMMANDS.put($it.toLowerCase(), ((DiscordCommand) aClass.newInstance()));
                    } catch (InstantiationException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                });
            }
            LOGGER.info("Loaded: " + COMMANDS.size() + " discord commands");
        }
    
        //must run on a debug mode on your IDE (or jrebel)
        public static void reload() {
            int commands = COMMANDS.size();
            init();
            LOGGER.info("Reloaded " + (COMMANDS.size() - commands) + " commands.");
        }
    
        public static void handleCommand(IChannel channel, IUser user, String input) {
            //(advance this by disallowing this to run through the game thread)
            System.out.println("Input: " + input + "; " + user + " " + channel.getName() + "  Thread: " + Thread.currentThread().getName());
    
            //fetches the command by the parser
            DiscordCommand command = COMMANDS.get(input);
            //check if command exists
            if (command == null) {
                DiscordAPI.message(channel, "The command ::" + input + " is invalid");
                return;
            } else {
                final DiscordCommandSettings annotation = command.getClass().getAnnotation(DiscordCommandSettings.class);
                if (annotation != null) {
                    try {
                        //check if player is privileged
                        if (annotation.privilege().getPosition() <= 0) {
                            command.action(channel, user, input);
                            return;
                        } else {
                            for (IRole role : user.getRolesForGuild(channel.getGuild())) {
                                if (role.getPosition() >= annotation.privilege().getPosition()) {
                                    command.action(channel, user, input);
                                    return;
                                }
                            }
                        }
                        //player has insufficient rights to use this command
                        DiscordAPI.message(channel, "You do not have the permissions to use this command.");
                        return;
                    } catch (Exception e) {
                        e.printStackTrace();
                        if (!annotation.syntaxPrefix().isEmpty()) {
    
                            DiscordAPI.message(channel, annotation.syntaxPrefix());
                            return;
                        } else {
                            DiscordAPI.message(channel, DEFAULT_SYNTAX_ERROR_MESSAGE.replace("{INPUT}", input));
                            return;
                        }
                    }
                }
            }
            DiscordAPI.message(channel, DEFAULT_SYNTAX_ERROR_MESSAGE.replace("{INPUT}", input));
        }
    }
    Code:
    package com.runeavion.core.network.discord.commands;
    
    import netscape.security.Privilege;
    import sx.blah.discord.handle.obj.IRole;
    
    import javax.annotation.Nonnull;
    import javax.annotation.Nullable;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * Discord command settings
     * @author Tyrant
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface DiscordCommandSettings {
    
        /**
         * The String inputs that may access this command
         */
        @Nonnull
        String[] accessInputs();
    
        /**
         * A short line explaining what is the purpose/usage of this command
         */
        @Nonnull
        String info();
    
        /**
         * The lowest {@link IRole#getPosition()} for using this command
         */
        @Nullable
        DiscordPrivilege privilege();
    
        /**
         * What message should be displayed on exception (e.g wrong format usage) when using this command.
         * If no input is included, then will show default error.
         */
        @Nullable
        String syntaxPrefix();
    
    }
    Code:
    package com.runeavion.core.network.discord.commands;
    
    /**
     * A privilege
     *
     * @author Tyrant
     * @date 3/7/2018
     */
    public enum DiscordPrivilege {
        EVERYBODY(-1);
    
        private final int position;
    
        DiscordPrivilege(int position) {
            this.position = position;
        }
    
        public int getPosition() {
            return position;
        }
    }

    some command tests; obviously just examples and code shouldn't be used (probably)

    Code:
    package com.runeavion.core.network.discord.commands.impl;
    
    import com.runeavion.core.network.discord.DiscordAPI;
    import com.runeavion.core.network.discord.commands.DiscordCommand;
    import com.runeavion.core.network.discord.commands.DiscordCommandSettings;
    import com.runeavion.core.network.discord.commands.DiscordPrivilege;
    import sx.blah.discord.handle.obj.IChannel;
    import sx.blah.discord.handle.obj.IUser;
    
    /**
     * A test command
     *
     * @author Tyrant
     * @date 3/7/2018
     */
    @DiscordCommandSettings(accessInputs = "test", info = "Test command", privilege = DiscordPrivilege.EVERYBODY, syntaxPrefix = "No no no... use it as ::test")
    public class TestDiscordCommand implements DiscordCommand {
    
        @Override
        public void action(Object... args) {
            IChannel channel = (IChannel) args[0];
            IUser user = (IUser) args[1];
            String input = (String) args[2];
    
            DiscordAPI.message(channel,"Hello there, " + user.getDisplayName(channel.getGuild()) + "... :smile:");
        }
    }

    Code:
    package com.runeavion.core.network.discord.commands.impl;
    
    import com.runeavion.core.network.discord.DiscordAPI;
    import com.runeavion.core.network.discord.commands.DiscordCommand;
    import com.runeavion.core.network.discord.commands.DiscordCommandSettings;
    import com.runeavion.core.network.discord.commands.DiscordPrivilege;
    import com.runeavion.util.RandomUtils;
    import sx.blah.discord.handle.obj.IChannel;
    import sx.blah.discord.handle.obj.IUser;
    
    import java.util.Random;
    import java.util.concurrent.ThreadLocalRandom;
    
    /**
     * A test command
     *
     * @author Tyrant
     * @date 3/7/2018
     */
    @DiscordCommandSettings(accessInputs = "roll", info = "Roll between 1-10", privilege = DiscordPrivilege.EVERYBODY, syntaxPrefix = "")
    public class RollDiscordCommand implements DiscordCommand {
    
        @Override
        public void action(Object... args) {
            IChannel channel = (IChannel) args[0];
            IUser user = (IUser) args[1];
    
            DiscordAPI.message(channel, user.getDisplayName(channel.getGuild()) + " rolled a " + ThreadLocalRandom.current().nextInt(10));
        }
    }




    If I've left anything, let me know.
    Remember, this will be updated eventually so check it up

    Last edited by Tyrant; 04-24-2018 at 01:17 PM.
    Reply With Quote  
     

  2. Thankful user:


  3. #2  
    Respected Member


    Kris's Avatar
    Join Date
    Jun 2016
    Age
    26
    Posts
    3,638
    Thanks given
    820
    Thanks received
    2,642
    Rep Power
    5000
    Attached image


    Seems solid.
    Attached image
    Reply With Quote  
     

  4. Thankful users:


  5. #3  
    Software Developer

    Tyrant's Avatar
    Join Date
    Jul 2013
    Age
    24
    Posts
    1,562
    Thanks given
    678
    Thanks received
    423
    Rep Power
    1060
    Quote Originally Posted by Kris View Post
    Attached image


    Seems solid.
    gg ez $50. The concept is really easy, I just hate people who rip others for things they don't even do (this is an implementation of the actual API)
    Reply With Quote  
     

  6. #4  
    Registered Member
    Quavo's Avatar
    Join Date
    Mar 2018
    Posts
    255
    Thanks given
    54
    Thanks received
    156
    Rep Power
    2116
    Nice contribution
    Reply With Quote  
     

  7. #5  
    Blurite

    Corey's Avatar
    Join Date
    Feb 2012
    Age
    26
    Posts
    1,491
    Thanks given
    1,245
    Thanks received
    1,729
    Rep Power
    5000
    Solid contribution, however the people who would buy a discord bot are the people who wouldn't be able to write this sort of system themselves, and most likely wouldn't be able to implement this system themselves either.
    For people who could write this themselves, it saves a lot of time and is a good framework to build upon.

    Going back to what I said, you could charge people for a custom implementation of this system - I'm almost certain there will still be a demand for it.
    Attached image
    Reply With Quote  
     

  8. #6  
    Donator
    Eric's Avatar
    Join Date
    Feb 2015
    Posts
    335
    Thanks given
    26
    Thanks received
    53
    Rep Power
    50
    Solid contribution to the community, thanks man !
    Reply With Quote  
     

  9. #7  
    So when I'm free, I'm free


    Jay Gatsby's Avatar
    Join Date
    Jun 2010
    Posts
    2,307
    Thanks given
    1,148
    Thanks received
    1,982
    Rep Power
    5000
    Nice job buddy, same lib Gielinor uses for our bot

    Hopefully with this more creative bot ideas will start cropping up!
    Reply With Quote  
     

  10. #8  
    Blurite

    Corey's Avatar
    Join Date
    Feb 2012
    Age
    26
    Posts
    1,491
    Thanks given
    1,245
    Thanks received
    1,729
    Rep Power
    5000
    Quote Originally Posted by Jay Gatsby View Post
    same lib Gielinor uses for our bot
    incorrect my friend
    Attached image
    Reply With Quote  
     

  11. Thankful user:


  12. #9  
    So when I'm free, I'm free


    Jay Gatsby's Avatar
    Join Date
    Jun 2010
    Posts
    2,307
    Thanks given
    1,148
    Thanks received
    1,982
    Rep Power
    5000
    Quote Originally Posted by Tesla View Post
    incorrect my friend
    you missed the memo, get the ide's out again
    Reply With Quote  
     

  13. #10  
    Donator
    tsdm's Avatar
    Join Date
    Jul 2017
    Posts
    31
    Thanks given
    6
    Thanks received
    17
    Rep Power
    31
    I literally posted yesterday for someone to add discord support on my help CC I still wouldn't know how to add this tbh. But sure someone will do it for cheaper now
    Reply With Quote  
     

Page 1 of 2 12 LastLast

Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. small irc bot framework/api
    By veer in forum Application Development
    Replies: 9
    Last Post: 05-28-2010, 10:17 PM
  2. Best vps/dedi sellers?
    By ~Josh in forum Hosting
    Replies: 4
    Last Post: 05-14-2009, 01:35 AM
  3. Eversio - The RSPS API
    By blakeman8192 in forum RS2 Server
    Replies: 30
    Last Post: 02-20-2009, 11:46 AM
  4. Replies: 9
    Last Post: 08-06-2008, 10:20 PM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •