Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next Version PR #14

Merged
merged 34 commits into from
Oct 19, 2021
Merged

Next Version PR #14

merged 34 commits into from
Oct 19, 2021

Conversation

ShindouMihou
Copy link
Owner

@ShindouMihou ShindouMihou commented Aug 11, 2021

This is a heads-up for the next version of Velen which follows Javacord 3.4.0, to use this, please move to the Snapshot repository of Javacord:

  • Paginate Messages with Buttons will now remove the buttons once the time is up.
  • Javacord v3.4.0 compatiability.
  • "True" Hybrid Command Support (resolves Add a HybridEvent #12)
  • Interaction support for Paginate Helper.
  • Automated slash command updater (and register).

To use this PR, please use Jitpack to build the PR. Also to note, the snapshot repository of Javacord and the version numbering will be changed once the next version of Javacord is officially out.

@ShindouMihou ShindouMihou added this to the Next Version milestone Aug 11, 2021
@ShindouMihou ShindouMihou self-assigned this Aug 11, 2021
@ShindouMihou
Copy link
Owner Author

ShindouMihou commented Aug 14, 2021

🎉 True Hybrid Command Support

This version of Velen now officially supports TRUE Hybrid command which means you can now use a single handler for both slash commands and message commands.

An example of which is:

VelenCommand.ofHybrid("dtest", "This is a dtest command.", velen, (event, responder, user, args) -> {
            if(args.length() > 0 && args.get(0).asString().isPresent()) {
                responder.setContent("Hello " + args.get(0).asString().get()).respond();
            } else {
                responder.setContent("Hello World!").respond();
            }
}, SlashCommandOption.create(SlashCommandOptionType.STRING, "name", "Your name.", false)) .attach();

🅰️ Named Options for both Message and Slash

If you want to learn more about how to add named arguments or options for message commands, please check #14 (comment)

@ShindouMihou
Copy link
Owner Author

ShindouMihou commented Aug 16, 2021

🎁 Subcommand support!

Velen now supports subcommands with the newest commit, to access a subcommand, all you have to do is get the option then run it through asSubcommand() which should give you a structure similar to VelenHybridArguments.

  • How does subcommands work for message commands?
    Well, the answer to this is a bit weird but all we do for subcommands on message command is take what is after the index of the subcommand return them. What this means is you can actually fetch the arguments of a message command normally through the parent: VelenHybridArguments().get(...)

An example of fetching a subcommand would be:

(event, responder, user, args) -> {
            if(args.length() > 0 && args.get(0).asSubcommand().length() > 0 
                      && args.get(0).asSubcommand().get(0).asString().isPresent()) {
                responder.setContent("Hello " + args.get(0).asSubcommand().get(0).asString().get()).respond();
            }
}

@ShindouMihou
Copy link
Owner Author

ShindouMihou commented Aug 20, 2021

💘 A more automated slash command updater.

Velen now offers a new integrated module which allows you to automatically update slash commands in a more automated way, this module checks for any differences between slash commands (global ones) to the tiniest bit like options, names or descriptions then updates them accordingly.

There are two different modes to this: NORMAL and SOFT

  • Normal Mode
    • This mode will register any VelenCommand that isn't registered on Discord's side already while also updating the ones that have differing values (like Soft).
  • Soft Mode
    • This mode will not register any VelenCommadn that isn't registered on Discord's side but will update any that have differing values (like for example, if a VelenCommand finds an equivalent command on Discord's side but with a different description, or a different option. It will send an update to update that command).

📦 How to use?

To use this module, all you have to do is create a new SlashCommandChecker instance which takes in a DiscordApi and Velen instance (the DiscordApi can be any shard, if you are wondering since it shouldn't matter for Discord):

Velen velen = ...
DiscordApi api = ...

new SlashCommandChecker(api, SlashCommandCheckerMode.NORMAL).run(velen);

The method run(Velen) returns back a CompletableFuture<Integer> with the integer showing many commands were updated (and registered).

💭 Is this blocking?

Despite being a CompletableFuture, the initial collection of all slash commands from Discord will be a blocking statement and shouldn't take long (usually 1-2 seconds depending on your latency towards Discord's REST). In the first place, this method IS SUPPOSED TO BE USED AT STARTUP to keep everything up-to-date each restart.

Other than that, after the initial GET request towards Discord, everything will start to run asynchronously through a different threadpool (VelenThreadPool) to prevent any blockage with Javacord's side.

@ShindouMihou
Copy link
Owner Author

ShindouMihou commented Aug 20, 2021

📝 Interaction Support for Paginate

This version of Velen now supports interactions for Pagination Helper, please note that this change has introduced a major breaking change for any code that uses PaginateEvent, here is an example of the previous PaginateEvent:

new PaginateSimpleEvent<String> {
...
}

And here is how it looks in this new version:

new PaginateSimpleEvent<MessageBuilder, MessageCreateEvent, String> {
...
}
new PaginateSimpleEvent<InteractionOriginalResponseUpdater, InteractionCreateEvent, String> {
...
}

Your IDE should assist you into fixing this breaking change, don't worry.

💭 Why the sudden breaking change?

The sudden breaking change was to introduce this new interaction support while also reducing code duplication as much as possible, otherwise, you would be expecting at least a very fat package for this framework. You can find all the changes here.

@ShindouMihou
Copy link
Owner Author

😭 Another Breaking Change

There is another breaking change to Velen which involves the slash event but don't worry, you can quickly fix this through CTRL + F and CTRL + R, the change added SlashCommandCreateEvent to the onEvent for VelenSlashEvent which is needed to support Pagination with Interactions.

Old:

void onEvent(SlashCommandInteraction event, User user, VelenArguments args, 
           List<SlashCommandInteractionOption> options, InteractionImmediateResponseBuilder firstResponder);

New:

void onEvent(SlashCommandCreateEvent originalEvent, SlashCommandInteraction event, User user, VelenArguments args, 
            List<SlashCommandInteractionOption> options, InteractionImmediateResponseBuilder firstResponder);

@ShindouMihou
Copy link
Owner Author

ShindouMihou commented Oct 17, 2021

🌃 Named Options is now supported!

Velen now supports named options, or otherwise named as routed options, which as the name implies works similar to a web framework's router which allows message commands and hybrid commands to be named and be conditioned from the get-go. This is by far one of the better changes of Velen but it is also breaking for message commands which can be fixed easily.

📦 Syntax and Formats

To first understand how this all works, we have to look in depth of the syntax for the formats:

  • :[optionName] is how a placeholder for an option looks.
  • :[optionName::(choose,only,among,us)] is how a placeholder with limited options (ignore casing) looks, the user has to only pick among the options inside the ::(...). PLEASE NOTE THAT VELEN DOES NOT SUPPORT PLACING SPACES BETWEEN OPTIONS
  • :[optionName:(choose,only,among,us)] is similar to the one above this but with strict casing.
  • :[optionName:{^[0-9]}] is a regex pattern option, this first checks if the option in that index matches the regex.

You can only place one regex pattern checker and option limiter inside a option (you can have both though), with a full example of a format utilizing all looking like this:

quiz :[name] :[difficulty::(hard,medium,easy)] :[topic::(anime,politics)] :[amount:{^[0-9]}]

The format above tells Velen that the command should have 4 arguments, the first argument will be named as name and the second argument will be difficulty and should match either hard, medium or easy (case insensitive) with the third argument being named topic and should match either anime, politics (case insensitive) and lastly, the last argument should be named amount and should match the regex pattern: ^[0-9] which means it should be a digit.

The beauty of this entire thing is, you can have multiple formats and Velen will still accurately grab the correct option name but ensure that the name of the options are unique in each format otherwise Velen will pick up only the first case. An instance with multiple formats is:

quiz :[name] :[difficulty::(hard,medium,easy)] :[topic::(anime,politics)] :[amount:{^[0-9]}]
quiz :[name] :[difficulty::(hard,medium,easy)] :[amount:{^[0-9]}]
quiz :[name] :[topic::(anime,politics)] :[amount:{^[0-9]}]
quiz :[name] :[amount:{^[0-9]}]
quiz :[name]
quiz :[name] :[topic::(anime,politics)]  :[difficulty::(hard,medium,easy)] :[amount:{^[0-9]}]
quiz :[name] :[amount:{^[0-9]}] :[topic::(anime,politics)]  :[difficulty::(hard,medium,easy)]

If your command has all those formats, Velen will still be able to fetch the correct option name, so don't worry. 😄

💯 Adding formats to your commands and syntax

This is the required method to have this entire thing working, you have to add a format to your command during building using the VelenCommandBuilder#addFormats(...) method, for instance:

VelenCommand.of("test", "A test command.", velen, (event, message, user, args1, options) -> {
     String name = options.withName("name").orElse("Unknown Person");

      if (options.withName("condition").isPresent()) {
         message.reply("Good"+options.withName("condition").get()+", " + name);
      } else {
         message.reply("Hello " + name);
      }
}).addFormats("test :[name]")
.addFormats("test :[name] :[condition::(morning,night)]")
.attach();

The example above will reply with Hello <name> if there is a name present, otherwise replies with Hello Unknown Person and if condition is present and also is either morning or night then it will reply with Goodmorning <name> or Goodnight <name>.

Hybrid commands also work the same but with extra steps on getting the value of the option since Slash Commands supports several types like integer and others which Velen will automatically convert for you for Hybrid Commands, example of option grabbing with name on Velen would be: args.withName("name").get().asString().get().

😱 Breaking Change

The breaking change this time is that VelenEvent and VelenServerEvent now has an extra parameter from:

- void onEvent(MessageCreateEvent event, Message message, User user, String[] args);
+ void onEvent(MessageCreateEvent event, Message message, User user, String[] args, VelenRoutedOptions options);

@ShindouMihou
Copy link
Owner Author

ShindouMihou commented Oct 17, 2021

🏢 Of(type) syntax for arguments.

The of-type syntax for message options or arguments is another key-feature of Velen which allows bots using both hybrid and message commands to be able to fetch the right type of argument or option for their commands. For example, if you want to fetch a user, all you have to do is add :of(user) and Velen will automatically check it for you.

You can only add one :of(type) for each option as per common sense.

  • :[optionName:of(numeric))] which will mean this option must be of numerical (alias of :[optionName:{^[0-9]}])
  • :[$optionName:of(boolean)] which will mean this option must be of boolean (alias of :[optionName:{true|false})
  • :[optionName:of(user))] which will mean this option must be of user mention.
  • :[$optionName:of(channel)] which will mean this option must be of channel mention.
  • :[optionName:of(role))] which will mean this option must be of role mention.
  • :[$optionName:of(message)] which will mean this option must be of message link.
  • :[optionName:of(snowflake))] which will mean this option must be of Discord ID.
  • :[$optionName:of(emoji)] which will mean this option must be of emoji.
  • :[$optionName:of(webhook)] which will mean this option must be of webhook.

@ShindouMihou
Copy link
Owner Author

ShindouMihou commented Oct 18, 2021

🥇 An entirely new way of command initialization

All this time, we were stuck with thinking about how to initialize commands, should we keep doing the ordinary way which is to initialize commands via method builders or should we go with annotations? Those two have been the stale way of initializing Discord commands amongst Javacord frameworks, and truthfully, I find both ugly to look at.

And so, I went ahead and brainstormed before coming up with .velen files which is Velen's newest and will be the recommended way of initializing commands in Velen. An example of how a .velen file looks is as shown below:

In-depth explanation will come later, please note that it supports subcommand, subcommand groups, choices and options all together and can translate everything automatically to message commands

&[misc]: category

# Ordinary command test.
&[ping]: hybrid {
    desc: Ping Pong!

    usage: ping
    shortcut: pin
    handler: hybrid.ping
    # The cooldown in milliseconds.
    cooldown: 5000
}

# Option testing with required.
&[say]: hybrid {
    desc: Say something for me!

    usage: say [text]

    &[text]: option {
        desc: The text you want the bot to say.
        required: true
        type: string
        has_many: true
    }

    handler: hybrid.say
}

# Choice testing.
&[scream]: hybrid {
    desc: I want to scream a number!
    usage: scream [AH|YA]

    &[scream]: option {
        type: string

        # This is because Velen requires hybrid commands to be of [Choice Name, Choice Value]
        # because of Discord Slash Command Specifications.
        choice: [AH, AH]
        choice: [YA, YA]
        required: true
        desc: The scream type.
    }
    handler: hybrid.scream
}

# Regex testing.
&[regex]: hybrid {
    desc: This is to test a regex.
    usage: regex [url]
    &[url]: option {
        type: string
        desc: The URL to respond back, must contain http or https.
        regex: ^(http|https)
    }
    handler: hybrid.regex
}

# Numeric type testing.
&[number]: hybrid {
    desc: Make me say a number!
    usage: number [number]

    &[number]: option {
        type: numeric
        required: true
        desc: The number you want me to say.
    }

    handler: hybrid.number
}

# User type option testing.
&[hi]: hybrid {
    desc: Say hello to someone.
    usage: hi [user]
    usage: hi

    handler: hybrid.hi
    &[user]: option {
        type: user
        required: false
        desc: The user you want to mention.
    }
}

You can read more about the examples here like how the handlers are setup and etc: https://github.com/ShindouMihou/Velen/tree/development/examples/hybrid

@ShindouMihou
Copy link
Owner Author

ShindouMihou commented Oct 18, 2021

🪙 hasMany and getWithMany

Velen's new argument system now supports fetching of multiple values with the same name for message commands, take for instance, the say command with the format say :[text], normally that would only be able to fetch all the arguments of Hello World if it is quoted but now, by adding has_many: true on the option on your .velen file or simply appending :hasMany() on the format (say :[text:hasMany()]), Velen will be able to join all the multiple values with the same name into one.

You can see this on the newer examples of hybrid commands on the new system.

@ShindouMihou ShindouMihou merged commit 7efdaae into master Oct 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a HybridEvent
1 participant