-
Notifications
You must be signed in to change notification settings - Fork 29
Contributor's guide
Before we get started, here are a few general rules to keep in mind when contributing.
- Read the docs, especially those for Discord4J (and other libraries, if you come into contact with them).
- Don't cause errors.
- Avoid causing warnings.
- Follow existing conventions to the best of your ability/understanding.
- Detail your commit messages as much as you can.
If you know how to fork, clone, and import a GitHub project, you can safely skip this section. Otherwise, read on.
Getting started with ghost2 is a simple step-by step process. I'll be using IntelliJ IDEA for any examples, as it's my IDE of choice and (arguably) the most popular IDE on the market for Java. If you'd like to use a different IDE, feel free to, but you'll have to work out the equivalent process yourself.
ghost2 is currently built with Java 10 (see issue #7). The easiest way to obtain a Java 10 JDK is through an open JDK distributor, such as AdoptOpenJDK. AdoptOpenJDK releases do not supply an installer, unlike the Oracle JDK. All you have to do is unzip the files to a folder of your choice (usually C:\Program Files\Java\jdk-<version>).
It is also assumed that you already have Git installed on your system. If not, go to this page and download the appropriate installer for your platform.
-
To begin, you'll want to fork ghost2 so you can push to your own repository. Click the Fork button located at the top right of the project page and wait until you see your fork's page.
-
Click the Clone or download button and copy the link to your repository.
-
Open up IntelliJ and navigate to the welcome page. Click Check out from Version Control, paste in the repository URL, and click Clone. Wait for Git to finish cloning
-
IntelliJ will ask you if you want to open the project. Click Yes. An Import project from Gradle window should pop up.
-
Set the Gradle JVM to your JDK 10 install directory. If you would like to, enable auto-import. Leave the other settings as they are, and click OK.
-
Wait for the Gradle wrapper to download a Gradle distribution, resolve dependencies, build, and sync. This may take anywhere from 30 seconds to 10 minutes depending on what you already have downloaded/cached and how fast your network is. After that, you're done!
Basic module example
public class ModulePing extends Module {
public ModulePing() {
super(new ModuleInfo.Builder(ModulePing.class)
.withName("ping")
.withDescription("Check for bot responsiveness"));
}
@Override
public void invoke(CommandContext ctx) {
ctx.reply("Pong!");
}
}Creating a command module is relatively easy, which makes doing so a good beginner task that doesn't require deep knowledge of the bot. Every command in ghost2 extends Module, and so will any modules you write. Make sure you precisely adhere to the rules on writing a Module class or you're guaranteed to get an InvalidModuleException somewhere down the line.
Two things are contractually required from a Module subclass:
- A call to
super()in the constructor with aModuleInfo.Builder - An
invoke()implementation
Also required, although not contractually, is a publicly accessible no-args constructor.
Finding your way around ModuleInfo is the part of writing a Module that's not obvious at first glance. ModuleInfo contains all the metadata for a module, including its name, description, type (see CommandType), and other data. Name, description, and type are the three fields you should be most concerned with, as they're required for every ModuleInfo instance. Name and description, specifically, should be not-blank, meaning they're both not null and contain at least one non-whitespace character.
To construct a ModuleInfo object, you need to use the built-in ModuleInfo.Builder class. When you look at the arguments for the builder's constructor, you'll notice that it takes a Class<? extends Module>. Pass in the actual class of the Module you're creating here. After that, you can do what you want with the builder; parameters are set in the typical builder-pattern fashion via the with... methods. You can't and don't need to call build() after setting your fields, as it's called automatically by the Module superclass.
Note that you also can't and don't need to set a CommandType parameter. That's what the Class argument in the constructor is for. ModuleInfo.Builder uses this parameter to pull the @CommandPackage annotation from your module's package and uses the annotation's value as its type. This means that setting a CommandType is a matter of placing your module class in the right package.
After writing your constructor, you can move on to what your command actually does. invoke() is the method that gets called when your command is... well, invoked. After control is passed to your module, everything is up to you.
CommandContext offers basically everything you could possibly need when your command is invoked. Upon invocation, CommandDispatcher will construct a CommandContext instance for you and pass it in to invoke(). From there, you can use all the getter methods that CommandContext offers to create rich commands that respond fluently to most situations. CommandContext fields are null-checked on construction, so you shouldn't have to worry about null pointers at all. Refer to the JavaDocs to view the exhaustive list of fields that are available.
CommandContext also gives you some quickfire reply methods that just take a message string if you want to send a plaintext message. If you make use of them, be aware of your options:
-
CommandContext#reply()sends a message create request to Discord and doesn't wait for it to finish. -
CommandContext#replyBlocking()sends a message create request to discord and does wait for it to finish.