-
Notifications
You must be signed in to change notification settings - Fork 29
Writing a command module
**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(@NotNull final 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:
-
CommandContext#reply()sends a message to the channel the command was invoked in. -
CommandContext#replyDirect()sends a message directly to the used who invoked the command.
-
Mark your
Moduleclasses asfinal. Inadvertently extending an implementedModuleeffectively creates a duplicate command entry with separate code.CommandRegistrywill handle duplicate command entries and duplicate names by the time 1.0 is finished, but marking your classesfinalis still a good practice. -
Use the
@ReflectiveAccessannotation where necessary. Reflection is utilized extensively throughout ghost2, both by internal classes and Spring. In many cases, your IDE won't detect that a member is accessed reflectively. In these cases, you can mark it with@ReflectiveAccessand configure your IDE to suppress "Unused declaration" and other related warnings for members marked with the annotation.