Skip to content

cloud-discord4j#

Cloud integration for Discord4J slash commands.

An example bot using cloud-discord4j can be found here.

Installation#

Cloud for Discord4J is available through Maven Central.

<dependencies>
    <dependency>
        <groupId>org.incendo</groupId>
        <artifactId>cloud-discord4j</artifactId>
        <version>1.0.0-beta.2</version>
    </dependency>
</dependencies>
implementation("org.incendo:cloud-discord4j:1.0.0-beta.2")
implementation 'org.incendo:cloud-discord4j:1.0.0-beta.2'

Usage#

Command Manager#

// Using the "native" Discord4JInteraction sender type:
Discord4JCommandManager<Discord4JInteraction> commandManager = new Discord4JCommandManager<>(
    executionCoordinator,
    Discord4JInteraction.InteractionMapper.identity()
);

// Using a custom sender type:
Discord4JCommandManager<YourSenderType> commandManager = new Discord4JCommandManager<>(
    executionCoordinator,
    interaction -> yourSenderType
);

where executionCoordinator is an ExecutionCoordinator instance.

You then register the command manager as a Discord4J event listener. Example:

discordClient.withGateway(commandManager::installEventListener).block();

The event listener handles command synchronization, command execution and autocompletion.

Parsers#

Mappings to all existing option types are supported:

  • INTEGER: IntegerParser
  • NUMBER: DoubleParser
  • BOOLEAN: BooleanParser
  • STRING: StringParser
  • USER: Discord4JParser.userParser()
  • CHANNEL: Discord4JParser.channelParser()
  • ROLE: Discord4JParser.roleParser()
  • MENTIONABLE: Discord4JParser.mentionableParser()
  • ATTACHMENT: Discord4JParser.attachmentParser()

Subcommands and subcommand groups are mapped to Cloud literals. Other parsers are by default be mapped to OptionType.STRING.

Choices#

The suggestion providers will be invoked for option types that support auto completions. You may also use constant choices by using DiscordChoices as the suggestion provider. Example:

commandBuilder.required(
    "integer",
    integerParser(),
    DiscordChoices.integers(1, 2, 3)
)

Choices may be used for custom parsers that get mapped to OptionType.STRING.

Reactive Execution Handlers#

As Discord4J makes extensive use of reactor classes it is recommended that you make use of Discord4JCommandExecutionHandler.reactiveHandler(Function), which allows you to return a Producer from your command handler. Example:

 .handler(reactiveHandler(context -> {
    final Discord4JInteraction interaction = context.sender();
    final String message = context.get(COMPONENT_MESSAGE);
    return interaction.commandEvent()
            .map(event -> (Mono<?>) event.reply(message).withEphemeral(true))
            .orElseGet(Mono::empty);
})

If you use annotated commands then you may return CompletableFuture<Void>. You’ll have to convert the reactor classes to futures. Example:

@Command("command")
public CompletableFuture<Void> command(
  final Discord4JInteraction interaction
) {
        return interaction.commandEvent()
                .map(event -> (Mono<?>) event.reply("Hello world"))
                .orElseGet(Mono::empty)
                .then()
                .toFuture();
}

Command Scopes#

You may choose where a command is registered to by using CommandScope. You apply the scope to the command builder using:

// Available everywhere (DMs, guilds, etc):
commandBuilder.apply(CommandScope.global());

// Available in all guilds:
commandBuilder.apply(CommandScope.guilds());

// Available in specific guilds:
commandBuilder.apply(CommandScope.guilds(some, guild, ids));

You may implement custom filtering by overriding the command scope predicate for the command factory:

commandManager.commandFactory()
        .commandScopePredicate((node, scope) -> yourLogicHere);

Annotations:#

If using annotated commands you may use the @CommandScope annotation. You must first install the builder modifier:

CommandScopeBuilderModifier.install(annotationParser);

You may then use the annotation:

@CommandScope(guilds = { 1337 })
@Command("command")
public void yourCommand() { /* ... */ }

Permissions#

You may set the default permissions by using DiscordPermission.of(Long) as the command permission. Example:

// Using Discord4J's PermissionSet helper class.
commandBuilder.permission(DiscordPermission.of(PermissionSet.all().getRawValue()))

You may use ordinary permissions by setting the permission function in Discord4JCommandManager.

Settings#

You may modify certain behaviors using the DiscordSettings. You may do this by accessing the Configurable<DiscordSetting> instance using Discord4JCommandManager.discordSettings().