-
Notifications
You must be signed in to change notification settings - Fork 0
Custom Decorators
As Halpbot is built using Hartshorn, it's very easy to override the built-in decorators or to create entirely new decorators.
It is possible to override the built-in decorators by binding a custom implementation with a higher priority than the default implementation. All Halpbot default implementations use a priority of -1, so any priority of 0+ will be used preferentially when constructing the decorator. It's important to note that all decorators must have a @Bound constructor of an ActionInvokable<C> and the decorator annotation.
Example: Overriding the @Log decorator
The basic format any custom-decorators is like so:
// @Binds indicates that this implementation defines the LogDecorator and should be used in the factory rather than the default
// LogDecorator due to the higher priority
@Binds(value = LogDecorator.class, priority = 0)
// All Decorators take in a generic parameter 'C extends InvocationContext' as they can be used by any action,
// each of which have their own InvocationContext
public class CustomLogDecorator<C extends InvocationContext> extends LogDecorator<C>
{
// @Bound specifies that this constructor should be used by the factory to create this decorator
@Bound
public CustomLogDecorator(ActionInvokable<C> actionInvokable, Log log) {
super(actionInvokable, log);
}
}In this custom decorator, we can then override any of the ActionInvokable<C> methods. In this case, let's override the Invoke(C invocationContext) method and make it log the exact message sent (Assuming that the action was invoked using a MessageReceivedEvent).
Note: Actions aren't limited to message commands, such as buttons, so it's important to check the instance of the event rather than just calling
HalpbotEvent#event(Class<?>)unless you're entirely certain.
@Override
public <R> Exceptional<R> invoke(C invocationContext) {
HalpbotEvent halpbotEvent = invocationContext.halpbotEvent();
// If the invocation context is created internally - Rather than via an event listener,
// the halpbot event may be null
if (halpbotEvent != null && halpbotEvent.rawEvent() instanceof MessageReceivedEvent messageEvent) {
this.logLevel().log(invocationContext.applicationContext(),
"[%s] %s".formatted(messageEvent.getClass().getSimpleName(), messageEvent.getMessage().getContentRaw()));
}
// Invoke the old decorator like normally
return super.invoke(invocationContext);
}With this new decorator, when the following command is called, you get the following logged in the console:
@Log(LogLevel.INFO)
@Command(description = "Tests the @Log decorator")
public String log() {
return "This command is logged when it is invoked";
}16:08:51.008 [ inWS-ReadThread] @ 13236 -> n.p.halpbot.decorators.log.LogLevel INFO - [MessageReceivedEvent] $log
16:08:51.009 [ inWS-ReadThread] @ 13236 -> n.p.halpbot.decorators.log.LogLevel INFO - [Bot Testing][general] pumbas600#7051 has invoked the action HalpbotCommands#log()
If you wanted to avoid invoking the overridden decorator (Instead calling the actual action itself - or the next decorator if there are multiple on the action) then you can do this by calling it from the decorated ActionInvokable<C> like so:
return this.actionInvokable().invoke(invocationContext);Sometimes, like in @Cooldown and @Permission, you don't want to invoke the action at all if certain conditions aren't met, however, you still want to display a message to the user informing them the reason why the action wasn't performed. In these situations, you can return an Exceptional containing an ExplainedException with any object and it will be displayed temporarily.
For example, consider this snippet from the PermissionDecorator:
@Override
public <R> Exceptional<R> invoke(C invocationContext) {
HalpbotEvent event = invocationContext.halpbotEvent();
if (event == null || this.permissionService.hasPermissions(event.user().getIdLong(), this.permissions)) {
return super.invoke(invocationContext);
}
return Exceptional.of(new ExplainedException("You do not have permission to use this command"));
}- Built-in Commands
- @Command Parameters
- Arguments
- Annotations
- Custom Objects
- Custom TypeParsers
- Slash Commands - W.I.P.
- Pagination - W.I.P