New slash commands API. subpackages directories are now consistent with names
This commit is contained in:
parent
1c30e31e63
commit
02e71ab27a
44
.github/workflows/tests.yml
vendored
44
.github/workflows/tests.yml
vendored
|
@ -81,36 +81,36 @@ jobs:
|
||||||
uses: actions/checkout@v2.3.4
|
uses: actions/checkout@v2.3.4
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
working-directory: ./nyxx.commander
|
working-directory: ./nyxx_commander
|
||||||
run: dart pub get
|
run: dart pub get
|
||||||
|
|
||||||
- name: Analyze project source
|
- name: Analyze project source
|
||||||
working-directory: ./nyxx.commander
|
working-directory: ./nyxx_commander
|
||||||
run: dart analyze
|
run: dart analyze
|
||||||
|
|
||||||
- name: Compile tests
|
- name: Compile tests
|
||||||
working-directory: ./nyxx.commander/test
|
working-directory: ./nyxx_commander/test
|
||||||
run: dart2native commander-test.dart
|
run: dart2native commander-test.dart
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
working-directory: ./nyxx.commander/test
|
working-directory: ./nyxx_commander/test
|
||||||
run: ./commander-test.exe
|
run: ./commander-test.exe
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
|
|
||||||
- name: Generate docs
|
- name: Generate docs
|
||||||
working-directory: ./nyxx.commander
|
working-directory: ./nyxx_commander
|
||||||
run: dartdoc
|
run: dartdoc
|
||||||
|
|
||||||
- name: Deploy nyxx.commander dev docs
|
- name: Deploy nyxx_commander dev docs
|
||||||
uses: easingthemes/ssh-deploy@v2.1.5
|
uses: easingthemes/ssh-deploy@v2.1.5
|
||||||
env:
|
env:
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
|
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
|
||||||
ARGS: "-rltDzvO"
|
ARGS: "-rltDzvO"
|
||||||
SOURCE: "nyxx.commander/doc/api/"
|
SOURCE: "nyxx_commander/doc/api/"
|
||||||
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
|
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
|
||||||
REMOTE_USER: ${{ secrets.REMOTE_USER }}
|
REMOTE_USER: ${{ secrets.REMOTE_USER }}
|
||||||
TARGET: "${{ secrets.REMOTE_TARGET }}/nyxx.commander/"
|
TARGET: "${{ secrets.REMOTE_TARGET }}/nyxx_commander/"
|
||||||
|
|
||||||
test-extensions:
|
test-extensions:
|
||||||
name: Tests extensions package
|
name: Tests extensions package
|
||||||
|
@ -134,36 +134,36 @@ jobs:
|
||||||
uses: actions/checkout@v2.3.4
|
uses: actions/checkout@v2.3.4
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
working-directory: ./nyxx.extensions
|
working-directory: ./nyxx_extensions
|
||||||
run: dart pub get
|
run: dart pub get
|
||||||
|
|
||||||
- name: Analyze project source
|
- name: Analyze project source
|
||||||
working-directory: ./nyxx.extensions
|
working-directory: ./nyxx_extensions
|
||||||
run: dart analyze
|
run: dart analyze
|
||||||
|
|
||||||
- name: Compile tests
|
- name: Compile tests
|
||||||
working-directory: ./nyxx.extensions/test
|
working-directory: ./nyxx_extensions/test
|
||||||
run: dart2native extensions-tests.dart
|
run: dart2native extensions-tests.dart
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
working-directory: ./nyxx.extensions/test
|
working-directory: ./nyxx_extensions/test
|
||||||
run: ./extensions-tests.exe
|
run: ./extensions-tests.exe
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
|
|
||||||
- name: Generate docs
|
- name: Generate docs
|
||||||
working-directory: ./nyxx.extensions
|
working-directory: ./nyxx_extensions
|
||||||
run: dartdoc
|
run: dartdoc
|
||||||
|
|
||||||
- name: Deploy nyxx.extensions dev docs
|
- name: Deploy nyxx_extensions dev docs
|
||||||
uses: easingthemes/ssh-deploy@v2.1.5
|
uses: easingthemes/ssh-deploy@v2.1.5
|
||||||
env:
|
env:
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
|
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
|
||||||
ARGS: "-rltDzvO"
|
ARGS: "-rltDzvO"
|
||||||
SOURCE: "nyxx.extensions/doc/api/"
|
SOURCE: "nyxx_extensions/doc/api/"
|
||||||
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
|
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
|
||||||
REMOTE_USER: ${{ secrets.REMOTE_USER }}
|
REMOTE_USER: ${{ secrets.REMOTE_USER }}
|
||||||
TARGET: "${{ secrets.REMOTE_TARGET }}/nyxx.extensions/"
|
TARGET: "${{ secrets.REMOTE_TARGET }}/nyxx_extensions/"
|
||||||
|
|
||||||
test-interactions:
|
test-interactions:
|
||||||
name: Tests interactions package
|
name: Tests interactions package
|
||||||
|
@ -187,23 +187,23 @@ jobs:
|
||||||
uses: actions/checkout@v2.3.4
|
uses: actions/checkout@v2.3.4
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
working-directory: ./nyxx.interactions
|
working-directory: ./nyxx_interactions
|
||||||
run: dart pub get
|
run: dart pub get
|
||||||
|
|
||||||
- name: Analyze project source
|
- name: Analyze project source
|
||||||
working-directory: ./nyxx.interactions
|
working-directory: ./nyxx_interactions
|
||||||
run: dart analyze
|
run: dart analyze
|
||||||
|
|
||||||
- name: Generate docs
|
- name: Generate docs
|
||||||
working-directory: ./nyxx.interactions
|
working-directory: ./nyxx_interactions
|
||||||
run: dartdoc
|
run: dartdoc
|
||||||
|
|
||||||
- name: Deploy nyxx.interactions dev docs
|
- name: Deploy nyxx_interactions dev docs
|
||||||
uses: easingthemes/ssh-deploy@v2.1.5
|
uses: easingthemes/ssh-deploy@v2.1.5
|
||||||
env:
|
env:
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
|
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
|
||||||
ARGS: "-rltDzvO"
|
ARGS: "-rltDzvO"
|
||||||
SOURCE: "nyxx.interactions/doc/api/"
|
SOURCE: "nyxx_interactions/doc/api/"
|
||||||
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
|
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
|
||||||
REMOTE_USER: ${{ secrets.REMOTE_USER }}
|
REMOTE_USER: ${{ secrets.REMOTE_USER }}
|
||||||
TARGET: "${{ secrets.REMOTE_TARGET }}/nyxx.interactions/"
|
TARGET: "${{ secrets.REMOTE_TARGET }}/nyxx_interactions/"
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
import "package:nyxx/nyxx.dart";
|
|
||||||
import "package:nyxx_interactions/interactions.dart";
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
final bot = Nyxx("<%TOKEN%>", GatewayIntents.all);
|
|
||||||
|
|
||||||
final interactions = Interactions(bot);
|
|
||||||
|
|
||||||
interactions.registerCommand(interactions.createCommand(
|
|
||||||
"echo", // The command name
|
|
||||||
"echo a message", // The commands description
|
|
||||||
[CommandArg(CommandArgType.string, "message", "the message to be echoed.")], // The commands arguments
|
|
||||||
guild: Snowflake(""), // Replace with your guilds ID
|
|
||||||
));
|
|
||||||
|
|
||||||
bot.onReady.listen((event) {
|
|
||||||
interactions.sync(); // Sync commands with API
|
|
||||||
// Listen to slash commands being triggered
|
|
||||||
interactions.onSlashCommand.listen((event) async {
|
|
||||||
// Check if the name of the command is echo
|
|
||||||
if (event.interaction.name == "echo") {
|
|
||||||
// Reply with the message the user sent, showSource makes discord show the command the user sent in the channel.
|
|
||||||
await event.respond(content: event.interaction.getArg("message"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
import "package:nyxx/nyxx.dart";
|
|
||||||
import "package:nyxx_interactions/interactions.dart";
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
final bot = Nyxx("<%TOKEN%>", GatewayIntents.all);
|
|
||||||
final interactions = Interactions(bot);
|
|
||||||
|
|
||||||
interactions
|
|
||||||
..registerHandler("test", "This is test comamnd", [], handler: (event) async {
|
|
||||||
// Acknowledge about event so you can send reply later.
|
|
||||||
// You have 3 second to either ack command or send response
|
|
||||||
await event.acknowledge();
|
|
||||||
|
|
||||||
// After long running task, send response
|
|
||||||
await event.respond(content: "This is example message result");
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
part of nyxx_interactions;
|
|
||||||
|
|
||||||
typedef SlashCommandHandlder = FutureOr<void> Function(InteractionEvent);
|
|
||||||
|
|
||||||
/// Interaction extension for Nyxx. Allows use of: Slash Commands.
|
|
||||||
class Interactions {
|
|
||||||
static const _interactionCreateCommand = "INTERACTION_CREATE";
|
|
||||||
static const _op0 = 0;
|
|
||||||
|
|
||||||
late final Nyxx _client;
|
|
||||||
final Logger _logger = Logger("Interactions");
|
|
||||||
final List<SlashCommand> _commands = [];
|
|
||||||
late final _EventController _events;
|
|
||||||
|
|
||||||
/// Emitted when a slash command is sent.
|
|
||||||
late final Stream<InteractionEvent> onSlashCommand;
|
|
||||||
|
|
||||||
/// Emitted when a slash command is created by the user.
|
|
||||||
late final Stream<SlashCommand> onSlashCommandCreated;
|
|
||||||
|
|
||||||
final _commandHandlers = <String, SlashCommandHandlder>{};
|
|
||||||
|
|
||||||
/// Create new instance of the interactions class.
|
|
||||||
Interactions(Nyxx client) {
|
|
||||||
this._client = client;
|
|
||||||
_events = _EventController(this);
|
|
||||||
_client.options.dispatchRawShardEvent = true;
|
|
||||||
_logger.info("Interactions ready");
|
|
||||||
|
|
||||||
client.onReady.listen((event) async {
|
|
||||||
client.shardManager.rawEvent.listen((event) {
|
|
||||||
if (event.rawData["op"] as int == _op0) {
|
|
||||||
if (event.rawData["t"] as String == _interactionCreateCommand) {
|
|
||||||
_events.onSlashCommand.add(
|
|
||||||
InteractionEvent._new(client, event.rawData["d"] as Map<String, dynamic>),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this._commandHandlers.isNotEmpty) {
|
|
||||||
await this.sync();
|
|
||||||
this.onSlashCommand.listen((event) async {
|
|
||||||
try {
|
|
||||||
final handler = _commandHandlers[event.interaction.name];
|
|
||||||
|
|
||||||
if (handler == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await handler(event);
|
|
||||||
} on Error catch (e) {
|
|
||||||
this._logger.severe("Failed to execute command (${event.interaction.name})", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registers command and handler for that command.
|
|
||||||
void registerHandler(String name, String description, List<CommandArg> args, {required SlashCommandHandlder handler, Snowflake? guild}) {
|
|
||||||
final command = this.createCommand(name, description, args, guild: guild);
|
|
||||||
this.registerCommand(command);
|
|
||||||
_commandHandlers[name] = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a command that can be registered using .registerCommand or .registerCommands
|
|
||||||
///
|
|
||||||
/// The [name] is the name that the user can see when typing /, the [description] can also be seen in this same place. [args] are any arguments you want the user to type, you can put an empty list here is you require no arguments. If you want this to be specific to a guild you can set the [guild] param with the ID of a guild, when testing its recommended to use this as it propagates immediately while global commands can take some time.
|
|
||||||
SlashCommand createCommand(String name, String description, List<CommandArg> args, {Snowflake? guild}) =>
|
|
||||||
SlashCommand._new(_client, name, description, args,
|
|
||||||
guild: guild != null ? CacheUtility.createCacheableGuild(_client, guild) : null);
|
|
||||||
|
|
||||||
/// Registers a single command.
|
|
||||||
///
|
|
||||||
/// The command you want to register is the [command] you create a command by using [createCommand]
|
|
||||||
void registerCommand(SlashCommand command) => _commands.add(command);
|
|
||||||
|
|
||||||
/// Registers multiple commands at one.
|
|
||||||
///
|
|
||||||
/// The commands you want to register is the [commands] you create a command by using [createCommand], this just runs [registerCommand] for each command.
|
|
||||||
void registerCommands(List<SlashCommand> commands) => commands.forEach(this.registerCommand);
|
|
||||||
|
|
||||||
/// Gets all the commands that are currently registered.
|
|
||||||
List<SlashCommand> getCommands({bool registeredOnly = false}) {
|
|
||||||
if (!registeredOnly) {
|
|
||||||
return _commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _commands.where((command) => command.isRegistered).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Syncs the local commands with the discord API
|
|
||||||
Future<void> sync() async {
|
|
||||||
var success = 0;
|
|
||||||
var failed = 0;
|
|
||||||
for (final command in _commands) {
|
|
||||||
if (!command.isRegistered) {
|
|
||||||
try {
|
|
||||||
final registeredCommand = await command._register();
|
|
||||||
this._events.onSlashCommandCreated.add(registeredCommand);
|
|
||||||
|
|
||||||
success++;
|
|
||||||
} on HttpResponseError catch (e) {
|
|
||||||
this._logger.severe("Failed registering command: ${e.toString()}");
|
|
||||||
failed++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_logger.info(
|
|
||||||
"Successfully registered $success ${success > 1 ? "commands" : "command"}. Failed registering $failed ${failed > 1 ? "commands" : "command"}.");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
part of nyxx_interactions;
|
|
||||||
|
|
||||||
/// A slash command, can only be instantiated through a method on [Interactions]
|
|
||||||
class SlashCommand {
|
|
||||||
final Nyxx _client;
|
|
||||||
|
|
||||||
Snowflake? _id;
|
|
||||||
|
|
||||||
Snowflake get id {
|
|
||||||
if (!this.isRegistered || _id == null) {
|
|
||||||
throw new StateError("There is no id if command is not registered");
|
|
||||||
}
|
|
||||||
|
|
||||||
return _id!;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Command name to be shown to the user in the Slash Command UI
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
/// Command description shown to the user in the Slash Command UI
|
|
||||||
final String description;
|
|
||||||
|
|
||||||
/// The guild that the slash Command is registered in. This can be null if its a global command.
|
|
||||||
late final Cacheable<Snowflake, Guild>? guild;
|
|
||||||
|
|
||||||
/// The arguments that the command takes
|
|
||||||
final List<CommandArg> args;
|
|
||||||
|
|
||||||
/// If the command is a global on, false if restricted to a guild.
|
|
||||||
bool get isGlobal => this.guild == null;
|
|
||||||
|
|
||||||
/// If the command has been registered with the discord api
|
|
||||||
late bool isRegistered = false;
|
|
||||||
|
|
||||||
SlashCommand._new(this._client, this.name, this.description, this.args, {this.guild});
|
|
||||||
|
|
||||||
Future<SlashCommand> _register() async {
|
|
||||||
final options = args.map((e) => e._build()).toList();
|
|
||||||
|
|
||||||
var path = "/applications/${this._client.app.id.toString()}";
|
|
||||||
|
|
||||||
if (this.guild != null) {
|
|
||||||
path += "/guilds/${this.guild!.id}";
|
|
||||||
}
|
|
||||||
|
|
||||||
path += "/commands";
|
|
||||||
|
|
||||||
final response = await this._client.httpEndpoints.sendRawRequest(
|
|
||||||
path,
|
|
||||||
"POST",
|
|
||||||
body: {"name": this.name, "description": this.description, "options": options.isNotEmpty ? options : null},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response is HttpResponseError) {
|
|
||||||
return Future.error(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._id = Snowflake((response as HttpResponseSuccess).jsonBody["id"]);
|
|
||||||
this.isRegistered = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
part of nyxx_interactions;
|
|
||||||
|
|
||||||
/// The type that a user should input for a [CommandArg]
|
|
||||||
class CommandArgType extends IEnum<int> {
|
|
||||||
/// Specify an arg as a sub command
|
|
||||||
static const subCommand = const CommandArgType(1);
|
|
||||||
/// Specify an arg as a sub command group
|
|
||||||
static const subCommandGroup = const CommandArgType(2);
|
|
||||||
/// Specify an arg as a string
|
|
||||||
static const string = const CommandArgType(3);
|
|
||||||
/// Specify an arg as an int
|
|
||||||
static const integer = const CommandArgType(4);
|
|
||||||
/// Specify an arg as a bool
|
|
||||||
static const boolean = const CommandArgType(5);
|
|
||||||
/// Specify an arg as a user e.g @HarryET#2954
|
|
||||||
static const user = const CommandArgType(6);
|
|
||||||
/// Specify an arg as a channel e.g. #Help
|
|
||||||
static const channel = const CommandArgType(7);
|
|
||||||
/// Specify an arg as a role e.g. @RoleName
|
|
||||||
static const role = const CommandArgType(8);
|
|
||||||
|
|
||||||
/// Create new instance of CommandArgType
|
|
||||||
const CommandArgType(int value) : super(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An argument for a [SlashCommand].
|
|
||||||
class CommandArg implements Builder {
|
|
||||||
/// The type of arg that will be later changed to an INT value, their values can be seen in the table below:
|
|
||||||
/// | Name | Value |
|
|
||||||
/// |-------------------|-------|
|
|
||||||
/// | SUB_COMMAND | 1 |
|
|
||||||
/// | SUB_COMMAND_GROUP | 2 |
|
|
||||||
/// | STRING | 3 |
|
|
||||||
/// | INTEGER | 4 |
|
|
||||||
/// | BOOLEAN | 5 |
|
|
||||||
/// | USER | 6 |
|
|
||||||
/// | CHANNEL | 7 |
|
|
||||||
/// | ROLE | 8 |
|
|
||||||
late final CommandArgType type;
|
|
||||||
|
|
||||||
/// The name of your argument / sub-group.
|
|
||||||
late final String name;
|
|
||||||
|
|
||||||
/// The description of your argument / sub-group.
|
|
||||||
late final String description;
|
|
||||||
|
|
||||||
/// If this should be the fist required option the user picks
|
|
||||||
late final bool defaultArg;
|
|
||||||
|
|
||||||
/// If this argument is required
|
|
||||||
late final bool required;
|
|
||||||
|
|
||||||
/// Choices for [CommandArgType.string] and [CommandArgType.string] types for the user to pick from
|
|
||||||
late final List<ArgChoice>? choices;
|
|
||||||
|
|
||||||
/// If the option is a subcommand or subcommand group type, this nested options will be the parameters
|
|
||||||
late final List<CommandArg>? options;
|
|
||||||
|
|
||||||
/// Used to create an argument for a [SlashCommand]. Tease are used in [Interactions.registerCommand]
|
|
||||||
CommandArg(this.type, this.name, this.description,
|
|
||||||
{this.defaultArg = false, this.required = false, this.choices, this.options});
|
|
||||||
|
|
||||||
Map<String, dynamic> _build() => {
|
|
||||||
"type": this.type.value,
|
|
||||||
"name": this.name,
|
|
||||||
"description": this.description,
|
|
||||||
"default": this.defaultArg,
|
|
||||||
"required": this.required,
|
|
||||||
if (this.choices != null) "choices": this.choices!.map((e) => e._build()).toList(),
|
|
||||||
if (this.options != null) "options": this.options!.map((e) => e._build()).toList()
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
# nyxx.commander
|
# nyxx_commander
|
||||||
|
|
||||||
[![pub](https://img.shields.io/pub/v/nyxx.svg)](https://pub.dartlang.org/packages/nyxx)
|
[![pub](https://img.shields.io/pub/v/nyxx.svg)](https://pub.dartlang.org/packages/nyxx)
|
||||||
[![documentation](https://img.shields.io/badge/Documentation-nyxx-yellow.svg)](https://www.dartdocs.org/documentation/nyxx/latest/)
|
[![documentation](https://img.shields.io/badge/Documentation-nyxx-yellow.svg)](https://www.dartdocs.org/documentation/nyxx/latest/)
|
|
@ -1,4 +1,4 @@
|
||||||
# nyxx.extensions
|
# nyxx_extensions
|
||||||
|
|
||||||
[![pub](https://img.shields.io/pub/v/nyxx.svg)](https://pub.dartlang.org/packages/nyxx)
|
[![pub](https://img.shields.io/pub/v/nyxx.svg)](https://pub.dartlang.org/packages/nyxx)
|
||||||
[![documentation](https://img.shields.io/badge/Documentation-nyxx-yellow.svg)](https://www.dartdocs.org/documentation/nyxx/latest/)
|
[![documentation](https://img.shields.io/badge/Documentation-nyxx-yellow.svg)](https://www.dartdocs.org/documentation/nyxx/latest/)
|
|
@ -1,4 +1,4 @@
|
||||||
# nyxx.interactions
|
# nyxx_interactions
|
||||||
|
|
||||||
[![pub](https://img.shields.io/pub/v/nyxx.svg)](https://pub.dartlang.org/packages/nyxx)
|
[![pub](https://img.shields.io/pub/v/nyxx.svg)](https://pub.dartlang.org/packages/nyxx)
|
||||||
[![documentation](https://img.shields.io/badge/Documentation-nyxx-yellow.svg)](https://www.dartdocs.org/documentation/nyxx/latest/)
|
[![documentation](https://img.shields.io/badge/Documentation-nyxx-yellow.svg)](https://www.dartdocs.org/documentation/nyxx/latest/)
|
27
nyxx_interactions/example/interactions-basic.dart
Normal file
27
nyxx_interactions/example/interactions-basic.dart
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import "package:nyxx/nyxx.dart";
|
||||||
|
import "package:nyxx_interactions/interactions.dart";
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// final bot = Nyxx("<%TOKEN%>", GatewayIntents.all);
|
||||||
|
//
|
||||||
|
// final interactions = Interactions(bot);
|
||||||
|
//
|
||||||
|
// interactions.registerCommand(SlashCommandBuilder(
|
||||||
|
// "echo", // The command name
|
||||||
|
// "echo a message", // The commands description
|
||||||
|
// [CommandOptionBuilder(CommandOptionType.string, "message", "the message to be echoed.")], // The commands arguments
|
||||||
|
// guild: Snowflake(""), // Replace with your guilds ID
|
||||||
|
// ));
|
||||||
|
//
|
||||||
|
// bot.onReady.listen((event) {
|
||||||
|
// interactions.sync(); // Sync commands with API
|
||||||
|
// // Listen to slash commands being triggered
|
||||||
|
// interactions.onSlashCommand.listen((event) async {
|
||||||
|
// // Check if the name of the command is echo
|
||||||
|
// if (event.interaction.name == "echo") {
|
||||||
|
// // Reply with the message the user sent, showSource makes discord show the command the user sent in the channel.
|
||||||
|
// await event.respond(content: event.interaction.getArg("message"));
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
}
|
|
@ -12,13 +12,19 @@ part "src/Interactions.dart";
|
||||||
part "src/models/SlashCommand.dart";
|
part "src/models/SlashCommand.dart";
|
||||||
part "src/models/Interaction.dart";
|
part "src/models/Interaction.dart";
|
||||||
part "src/models/InteractionOption.dart";
|
part "src/models/InteractionOption.dart";
|
||||||
|
part "src/models/ArgChoice.dart";
|
||||||
|
|
||||||
|
// Builders
|
||||||
|
part "src/builders/ArgChoiceBuilder.dart";
|
||||||
|
part "src/builders/CommandOptionBuilder.dart";
|
||||||
|
part "src/builders/SlashCommandBuilder.dart";
|
||||||
|
|
||||||
// Command Args
|
// Command Args
|
||||||
part "src/models/commandArgs/ArgChoice.dart";
|
part "src/models/CommandOption.dart";
|
||||||
part "src/models/commandArgs/CommandArg.dart";
|
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
part "src/internal/_EventController.dart";
|
part "src/internal/_EventController.dart";
|
||||||
|
part "src/internal/utils.dart";
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
part "src/events/InteractionEvent.dart";
|
part "src/events/InteractionEvent.dart";
|
152
nyxx_interactions/lib/src/Interactions.dart
Normal file
152
nyxx_interactions/lib/src/Interactions.dart
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
part of nyxx_interactions;
|
||||||
|
|
||||||
|
typedef SlashCommandHandlder = FutureOr<void> Function(InteractionEvent);
|
||||||
|
|
||||||
|
/// Interaction extension for Nyxx. Allows use of: Slash Commands.
|
||||||
|
class Interactions {
|
||||||
|
static const _interactionCreateCommand = "INTERACTION_CREATE";
|
||||||
|
static const _op0 = 0;
|
||||||
|
|
||||||
|
final Nyxx _client;
|
||||||
|
late final _EventController _events;
|
||||||
|
|
||||||
|
final Logger _logger = Logger("Interactions");
|
||||||
|
|
||||||
|
final _commandBuilders = <SlashCommandBuilder>[];
|
||||||
|
final _commands = <SlashCommand>[];
|
||||||
|
final _commandHandlers = <String, SlashCommandHandlder>{};
|
||||||
|
|
||||||
|
/// Emitted when a slash command is sent.
|
||||||
|
late final Stream<InteractionEvent> onSlashCommand;
|
||||||
|
|
||||||
|
/// Emitted when a slash command is created by the user.
|
||||||
|
late final Stream<SlashCommand> onSlashCommandCreated;
|
||||||
|
|
||||||
|
/// Create new instance of the interactions class.
|
||||||
|
Interactions(this._client) {
|
||||||
|
_events = _EventController(this);
|
||||||
|
_client.options.dispatchRawShardEvent = true;
|
||||||
|
_logger.info("Interactions ready");
|
||||||
|
|
||||||
|
_client.onReady.listen((event) async {
|
||||||
|
_client.shardManager.rawEvent.listen((event) {
|
||||||
|
if (event.rawData["op"] == _op0
|
||||||
|
&& event.rawData["t"] == _interactionCreateCommand
|
||||||
|
) {
|
||||||
|
_events.onSlashCommand.add(InteractionEvent._new(_client, event.rawData["d"] as Map<String, dynamic>));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void syncOnReady() {
|
||||||
|
this._client.onReady.listen((_) async {
|
||||||
|
await this.sync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Syncs command builders with discord
|
||||||
|
Future<void> sync() async {
|
||||||
|
final commandPartition = _partition<SlashCommandBuilder>(this._commandBuilders, (element) => element.guild == null);
|
||||||
|
final globalCommands = commandPartition.first;
|
||||||
|
final groupedGuildCommands = _groupSlashCommandBuilders(commandPartition.last);
|
||||||
|
|
||||||
|
final globalCommandsResponse = await this._client.httpEndpoints.sendRawRequest(
|
||||||
|
"/applications/${this._client.app.id}/commands",
|
||||||
|
"PUT",
|
||||||
|
body: [
|
||||||
|
for(final builder in globalCommands)
|
||||||
|
builder._build()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (globalCommandsResponse is HttpResponseSuccess) {
|
||||||
|
this._registerCommandHandlers(globalCommandsResponse, globalCommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(final entry in groupedGuildCommands.entries) {
|
||||||
|
final response = await this._client.httpEndpoints.sendRawRequest(
|
||||||
|
"/applications/${this._client.app.id}/guilds/${entry.key}/commands",
|
||||||
|
"PUT",
|
||||||
|
body: [
|
||||||
|
for(final builder in entry.value)
|
||||||
|
builder._build()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response is HttpResponseSuccess) {
|
||||||
|
this._registerCommandHandlers(response, entry.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._commandBuilders.clear(); // Cleanup after registering command since we don't need this anymore
|
||||||
|
this._logger.info("Finished bulk overriding slash commands");
|
||||||
|
|
||||||
|
if (this._commands.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onSlashCommand.listen((event) async {
|
||||||
|
final commandHash = _determineInteractionCommandHandler(event.interaction);
|
||||||
|
|
||||||
|
if (this._commandHandlers.containsKey(commandHash)) {
|
||||||
|
await this._commandHandlers[commandHash]!(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._logger.info("Finished registering ${this._commandHandlers.length} commands!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerSlashCommand(SlashCommandBuilder slashCommandBuilder) {
|
||||||
|
this._commandBuilders.add(slashCommandBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _registerCommandHandlers(HttpResponseSuccess response, Iterable<SlashCommandBuilder> builders) {
|
||||||
|
final registeredSlashCommands = (response.jsonBody as List<dynamic>).map((e) => SlashCommand._new(e as Map<String, dynamic>, this._client));
|
||||||
|
|
||||||
|
for(final registeredCommand in registeredSlashCommands) {
|
||||||
|
final matchingBuilder = builders.firstWhere((element) => element.name == registeredCommand.name);
|
||||||
|
this._assignCommandToHandler(matchingBuilder, registeredCommand);
|
||||||
|
|
||||||
|
this._commands.add(registeredCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _assignCommandToHandler(SlashCommandBuilder builder, SlashCommand command) {
|
||||||
|
final commandHashPrefix = "${command.id}|${command.name}";
|
||||||
|
|
||||||
|
final subCommands = builder.options.where((element) => element.type == CommandOptionType.subCommand);
|
||||||
|
if (subCommands.isNotEmpty) {
|
||||||
|
for (final subCommand in subCommands) {
|
||||||
|
if (subCommand._handler == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._commandHandlers["$commandHashPrefix${subCommand.name}"] = subCommand._handler!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final subCommandGroups = builder.options.where((element) => element.type == CommandOptionType.subCommandGroup);
|
||||||
|
if (subCommandGroups.isNotEmpty) {
|
||||||
|
for (final subCommandGroup in subCommandGroups) {
|
||||||
|
final subCommands = subCommandGroup.options?.where((element) => element.type == CommandOptionType.subCommand) ?? [];
|
||||||
|
|
||||||
|
for (final subCommand in subCommands) {
|
||||||
|
if (subCommand._handler == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._commandHandlers["$commandHashPrefix${subCommandGroup.name}${subCommand.name}"] = subCommand._handler!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builder._handler != null) {
|
||||||
|
this._commandHandlers[commandHashPrefix] = builder._handler!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,20 @@
|
||||||
part of nyxx_interactions;
|
part of nyxx_interactions;
|
||||||
|
|
||||||
/// A specified choice for a slash command argument.
|
/// A specified choice for a slash command argument.
|
||||||
class ArgChoice implements Builder {
|
class ArgChoiceBuilder implements Builder {
|
||||||
/// This options name.
|
/// This options name.
|
||||||
final String name;
|
String name;
|
||||||
|
|
||||||
/// This is the options value, must be int or string
|
/// This is the options value, must be int or string
|
||||||
final dynamic value;
|
dynamic value;
|
||||||
|
|
||||||
/// A Choice for the user to input in int & string args. You can only have an int or string option.
|
/// A Choice for the user to input in int & string args.
|
||||||
ArgChoice(this.name, this.value) {
|
/// You can only have an int or string option.
|
||||||
if (value is! int && value is! String) {
|
ArgChoiceBuilder(this.name, this.value) {
|
||||||
throw ArgumentError("Please send a string if its a string arg or an int if its an int arg");
|
if (value is! int && value is! String) {
|
||||||
}
|
throw ArgumentError("Please send a string if its a string arg or an int if its an int arg");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Map<String, dynamic> _build() => {"name": this.name, "value": this.value};
|
|
||||||
}
|
Map<String, dynamic> _build() => { "name": this.name, "value": this.value };
|
||||||
|
}
|
60
nyxx_interactions/lib/src/builders/CommandOptionBuilder.dart
Normal file
60
nyxx_interactions/lib/src/builders/CommandOptionBuilder.dart
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
part of nyxx_interactions;
|
||||||
|
|
||||||
|
/// An argument for a [SlashCommandBuilder].
|
||||||
|
class CommandOptionBuilder implements Builder {
|
||||||
|
/// The type of arg that will be later changed to an INT value, their values can be seen in the table below:
|
||||||
|
/// | Name | Value |
|
||||||
|
/// |-------------------|-------|
|
||||||
|
/// | SUB_COMMAND | 1 |
|
||||||
|
/// | SUB_COMMAND_GROUP | 2 |
|
||||||
|
/// | STRING | 3 |
|
||||||
|
/// | INTEGER | 4 |
|
||||||
|
/// | BOOLEAN | 5 |
|
||||||
|
/// | USER | 6 |
|
||||||
|
/// | CHANNEL | 7 |
|
||||||
|
/// | ROLE | 8 |
|
||||||
|
final CommandOptionType type;
|
||||||
|
|
||||||
|
/// The name of your argument / sub-group.
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// The description of your argument / sub-group.
|
||||||
|
final String description;
|
||||||
|
|
||||||
|
/// If this should be the fist required option the user picks
|
||||||
|
bool defaultArg = false;
|
||||||
|
|
||||||
|
/// If this argument is required
|
||||||
|
bool required = false;
|
||||||
|
|
||||||
|
/// Choices for [CommandOptionType.string] and [CommandOptionType.string] types for the user to pick from
|
||||||
|
List<ArgChoiceBuilder>? choices;
|
||||||
|
|
||||||
|
/// If the option is a subcommand or subcommand group type, this nested options will be the parameters
|
||||||
|
List<CommandOptionBuilder>? options;
|
||||||
|
|
||||||
|
SlashCommandHandlder? _handler;
|
||||||
|
|
||||||
|
/// Used to create an argument for a [SlashCommandBuilder].
|
||||||
|
CommandOptionBuilder(this.type, this.name, this.description,
|
||||||
|
{this.defaultArg = false, this.required = false, this.choices, this.options});
|
||||||
|
|
||||||
|
Map<String, dynamic> _build() => {
|
||||||
|
"type": this.type.value,
|
||||||
|
"name": this.name,
|
||||||
|
"description": this.description,
|
||||||
|
"default": this.defaultArg,
|
||||||
|
"required": this.required,
|
||||||
|
if (this.choices != null) "choices": this.choices!.map((e) => e._build()).toList(),
|
||||||
|
if (this.options != null) "options": this.options!.map((e) => e._build()).toList()
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Registers handler for subcommand
|
||||||
|
void registerHandler(SlashCommandHandlder handler) {
|
||||||
|
if (this.type != CommandOptionType.subCommand) {
|
||||||
|
throw StateError("Cannot register handler for command option with type other that subcommand");
|
||||||
|
}
|
||||||
|
|
||||||
|
this._handler = handler;
|
||||||
|
}
|
||||||
|
}
|
29
nyxx_interactions/lib/src/builders/SlashCommandBuilder.dart
Normal file
29
nyxx_interactions/lib/src/builders/SlashCommandBuilder.dart
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
part of nyxx_interactions;
|
||||||
|
|
||||||
|
/// A slash command, can only be instantiated through a method on [Interactions]
|
||||||
|
class SlashCommandBuilder implements Builder {
|
||||||
|
/// Command name to be shown to the user in the Slash Command UI
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// Command description shown to the user in the Slash Command UI
|
||||||
|
final String description;
|
||||||
|
|
||||||
|
/// The guild that the slash Command is registered in. This can be null if its a global command.
|
||||||
|
Snowflake? guild;
|
||||||
|
|
||||||
|
/// The arguments that the command takes
|
||||||
|
List<CommandOptionBuilder> options;
|
||||||
|
|
||||||
|
SlashCommandHandlder? _handler;
|
||||||
|
|
||||||
|
/// A slash command, can only be instantiated through a method on [Interactions]
|
||||||
|
SlashCommandBuilder(this.name, this.description, this.options, {this.guild});
|
||||||
|
|
||||||
|
Map<String, dynamic> _build() => {
|
||||||
|
"name": this.name,
|
||||||
|
"description": this.description,
|
||||||
|
if (this.options.isNotEmpty) "options": this.options.map((e) => e._build()).toList()
|
||||||
|
};
|
||||||
|
|
||||||
|
void registerHandler(SlashCommandHandlder handler) => this._handler = handler;
|
||||||
|
}
|
|
@ -13,19 +13,6 @@ class InteractionEvent {
|
||||||
/// If the Client has sent a response to the Discord API. Once the API was received a response you cannot send another.
|
/// If the Client has sent a response to the Discord API. Once the API was received a response you cannot send another.
|
||||||
bool hasResponded = false;
|
bool hasResponded = false;
|
||||||
|
|
||||||
/// Returns subcommand or null if not subcommand
|
|
||||||
InteractionOption? get subCommand {
|
|
||||||
if (this.interaction.args.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return this.interaction.args.firstWhere((element) => element.type == CommandArgType.subCommand);
|
|
||||||
} on Error {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InteractionEvent._new(this._client, Map<String, dynamic> rawJson) {
|
InteractionEvent._new(this._client, Map<String, dynamic> rawJson) {
|
||||||
this.interaction = Interaction._new(this._client, rawJson);
|
this.interaction = Interaction._new(this._client, rawJson);
|
||||||
|
|
50
nyxx_interactions/lib/src/internal/utils.dart
Normal file
50
nyxx_interactions/lib/src/internal/utils.dart
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
part of nyxx_interactions;
|
||||||
|
|
||||||
|
Iterable<Iterable<T>> _partition<T>(Iterable<T> list, bool Function(T) predicate) {
|
||||||
|
final matches = <T>[];
|
||||||
|
final nonMatches = <T>[];
|
||||||
|
|
||||||
|
for(final e in list) {
|
||||||
|
if(predicate(e)) {
|
||||||
|
matches.add(e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nonMatches.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [matches, nonMatches];
|
||||||
|
}
|
||||||
|
|
||||||
|
String _determineInteractionCommandHandler(Interaction interaction) {
|
||||||
|
final commandHash = "${interaction.commandId}|${interaction.name}";
|
||||||
|
|
||||||
|
try {
|
||||||
|
final subCommandGroup = interaction.args.firstWhere((element) => element.type == CommandOptionType.subCommandGroup);
|
||||||
|
final subCommand = interaction.args.firstWhere((element) => element.type == CommandOptionType.subCommand);
|
||||||
|
|
||||||
|
return "$commandHash${subCommandGroup.name}${subCommand.name}";
|
||||||
|
// ignore: empty_catches
|
||||||
|
} on Error { }
|
||||||
|
|
||||||
|
final subCommand = interaction.args.firstWhere((element) => element.type == CommandOptionType.subCommand);
|
||||||
|
|
||||||
|
return "$commandHash${subCommand.name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Snowflake, Iterable<SlashCommandBuilder>> _groupSlashCommandBuilders(Iterable<SlashCommandBuilder> commands) {
|
||||||
|
final commandsMap = <Snowflake, List<SlashCommandBuilder>>{};
|
||||||
|
|
||||||
|
for(final slashCommand in commands) {
|
||||||
|
final id = slashCommand.guild!;
|
||||||
|
|
||||||
|
if (commandsMap.containsKey(id)) {
|
||||||
|
commandsMap[id]!.add(slashCommand);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
commandsMap[id] = [slashCommand];
|
||||||
|
}
|
||||||
|
|
||||||
|
return commandsMap;
|
||||||
|
}
|
12
nyxx_interactions/lib/src/models/ArgChoice.dart
Normal file
12
nyxx_interactions/lib/src/models/ArgChoice.dart
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
part of nyxx_interactions;
|
||||||
|
|
||||||
|
class ArgChoice {
|
||||||
|
late final String name;
|
||||||
|
|
||||||
|
late final dynamic value;
|
||||||
|
|
||||||
|
ArgChoice._new(Map<String, dynamic> raw) {
|
||||||
|
this.name = raw["name"] as String;
|
||||||
|
this.value = raw["value"];
|
||||||
|
}
|
||||||
|
}
|
73
nyxx_interactions/lib/src/models/CommandOption.dart
Normal file
73
nyxx_interactions/lib/src/models/CommandOption.dart
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
part of nyxx_interactions;
|
||||||
|
|
||||||
|
/// The type that a user should input for a [CommandOptionBuilder]
|
||||||
|
class CommandOptionType extends IEnum<int> {
|
||||||
|
/// Specify an arg as a sub command
|
||||||
|
static const subCommand = const CommandOptionType(1);
|
||||||
|
/// Specify an arg as a sub command group
|
||||||
|
static const subCommandGroup = const CommandOptionType(2);
|
||||||
|
/// Specify an arg as a string
|
||||||
|
static const string = const CommandOptionType(3);
|
||||||
|
/// Specify an arg as an int
|
||||||
|
static const integer = const CommandOptionType(4);
|
||||||
|
/// Specify an arg as a bool
|
||||||
|
static const boolean = const CommandOptionType(5);
|
||||||
|
/// Specify an arg as a user e.g @HarryET#2954
|
||||||
|
static const user = const CommandOptionType(6);
|
||||||
|
/// Specify an arg as a channel e.g. #Help
|
||||||
|
static const channel = const CommandOptionType(7);
|
||||||
|
/// Specify an arg as a role e.g. @RoleName
|
||||||
|
static const role = const CommandOptionType(8);
|
||||||
|
|
||||||
|
/// Create new instance of CommandArgType
|
||||||
|
const CommandOptionType(int value) : super(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CommandOption {
|
||||||
|
/// The type of arg that will be later changed to an INT value, their values can be seen in the table below:
|
||||||
|
/// | Name | Value |
|
||||||
|
/// |-------------------|-------|
|
||||||
|
/// | SUB_COMMAND | 1 |
|
||||||
|
/// | SUB_COMMAND_GROUP | 2 |
|
||||||
|
/// | STRING | 3 |
|
||||||
|
/// | INTEGER | 4 |
|
||||||
|
/// | BOOLEAN | 5 |
|
||||||
|
/// | USER | 6 |
|
||||||
|
/// | CHANNEL | 7 |
|
||||||
|
/// | ROLE | 8 |
|
||||||
|
late final CommandOptionType type;
|
||||||
|
|
||||||
|
/// The name of your argument / sub-group.
|
||||||
|
late final String name;
|
||||||
|
|
||||||
|
/// The description of your argument / sub-group.
|
||||||
|
late final String description;
|
||||||
|
|
||||||
|
/// If this argument is required
|
||||||
|
late final bool required;
|
||||||
|
|
||||||
|
/// Choices for [CommandOptionType.string] and [CommandOptionType.string] types for the user to pick from
|
||||||
|
late final List<ArgChoice> choices;
|
||||||
|
|
||||||
|
/// If the option is a subcommand or subcommand group type, this nested options will be the parameters
|
||||||
|
late final List<CommandOption> options;
|
||||||
|
|
||||||
|
CommandOption._new(Map<String, dynamic> raw) {
|
||||||
|
this.type = CommandOptionType(raw["type"] as int);
|
||||||
|
this.name = raw["name"] as String;
|
||||||
|
this.description = raw["description"] as String;
|
||||||
|
this.required = raw["required"] as bool? ?? false;
|
||||||
|
|
||||||
|
this.choices = [
|
||||||
|
if (raw["choices"] != null)
|
||||||
|
for(final choiceRaw in raw["choices"])
|
||||||
|
ArgChoice._new(choiceRaw as Map<String, dynamic>)
|
||||||
|
];
|
||||||
|
|
||||||
|
this.options = [
|
||||||
|
if (raw["options"] != null)
|
||||||
|
for(final optionRaw in raw["options"])
|
||||||
|
CommandOption._new(optionRaw as Map<String, dynamic>)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ class Interaction extends SnowflakeEntity {
|
||||||
late final int type;
|
late final int type;
|
||||||
|
|
||||||
/// The guild the command was sent in.
|
/// The guild the command was sent in.
|
||||||
late final Cacheable<Snowflake, Guild> guild;
|
late final Cacheable<Snowflake, Guild>? guild;
|
||||||
|
|
||||||
/// The channel the command was sent in.
|
/// The channel the command was sent in.
|
||||||
late final Cacheable<Snowflake, TextChannel> channel;
|
late final Cacheable<Snowflake, TextChannel> channel;
|
||||||
|
@ -35,10 +35,14 @@ class Interaction extends SnowflakeEntity {
|
||||||
Interaction._new(this._client, Map<String, dynamic> raw) : super(Snowflake(raw["id"])) {
|
Interaction._new(this._client, Map<String, dynamic> raw) : super(Snowflake(raw["id"])) {
|
||||||
this.type = raw["type"] as int;
|
this.type = raw["type"] as int;
|
||||||
|
|
||||||
this.guild = CacheUtility.createCacheableGuild(
|
if (raw["guild_id"] != null) {
|
||||||
_client,
|
this.guild = CacheUtility.createCacheableGuild(
|
||||||
Snowflake(raw["guild_id"],),
|
_client,
|
||||||
);
|
Snowflake(raw["guild_id"]),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.guild = null;
|
||||||
|
}
|
||||||
|
|
||||||
this.channel = CacheUtility.createCacheableTextChannel(
|
this.channel = CacheUtility.createCacheableTextChannel(
|
||||||
_client,
|
_client,
|
|
@ -6,7 +6,7 @@ class InteractionOption {
|
||||||
late final dynamic value;
|
late final dynamic value;
|
||||||
|
|
||||||
/// Type of interaction
|
/// Type of interaction
|
||||||
late final CommandArgType type;
|
late final CommandOptionType type;
|
||||||
|
|
||||||
/// Name of option
|
/// Name of option
|
||||||
late final String name;
|
late final String name;
|
||||||
|
@ -15,19 +15,19 @@ class InteractionOption {
|
||||||
late final Iterable<InteractionOption> args;
|
late final Iterable<InteractionOption> args;
|
||||||
|
|
||||||
/// Option choices
|
/// Option choices
|
||||||
late final Iterable<ArgChoice> choices;
|
late final Iterable<ArgChoiceBuilder> choices;
|
||||||
|
|
||||||
InteractionOption._new(Map<String, dynamic> raw) {
|
InteractionOption._new(Map<String, dynamic> raw) {
|
||||||
this.value = raw["value"] as dynamic;
|
this.value = raw["value"] as dynamic;
|
||||||
this.name = raw["name"] as String;
|
this.name = raw["name"] as String;
|
||||||
this.type = CommandArgType(raw["type"] as int);
|
this.type = CommandOptionType(raw["type"] as int);
|
||||||
|
|
||||||
if (raw["options"] != null) {
|
if (raw["options"] != null) {
|
||||||
this.args = (raw["options"] as List<dynamic>).map((e) => InteractionOption._new(e as Map<String, dynamic>));
|
this.args = (raw["options"] as List<dynamic>).map((e) => InteractionOption._new(e as Map<String, dynamic>));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (raw["choices"] != null) {
|
if (raw["choices"] != null) {
|
||||||
this.choices = (raw["options"] as List<Map<String, dynamic>>).map((e) => ArgChoice(e["name"] as String, e["value"]));
|
this.choices = (raw["options"] as List<Map<String, dynamic>>).map((e) => ArgChoiceBuilder(e["name"] as String, e["value"]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
27
nyxx_interactions/lib/src/models/SlashCommand.dart
Normal file
27
nyxx_interactions/lib/src/models/SlashCommand.dart
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
part of nyxx_interactions;
|
||||||
|
|
||||||
|
class SlashCommand extends SnowflakeEntity {
|
||||||
|
/// Unique id of the parent application
|
||||||
|
late final Snowflake applicationId;
|
||||||
|
|
||||||
|
/// Command name to be shown to the user in the Slash Command UI
|
||||||
|
late final String name;
|
||||||
|
|
||||||
|
/// Command description shown to the user in the Slash Command UI
|
||||||
|
late final String description;
|
||||||
|
|
||||||
|
/// The arguments that the command takes
|
||||||
|
late final List<CommandOption> options;
|
||||||
|
|
||||||
|
SlashCommand._new(Map<String, dynamic> raw, Nyxx client): super(Snowflake(raw["id"])) {
|
||||||
|
this.applicationId = Snowflake(raw["application_id"]);
|
||||||
|
this.name = raw["name"] as String;
|
||||||
|
this.description = raw["description"] as String;
|
||||||
|
|
||||||
|
this.options = [
|
||||||
|
if (raw["options"] != null)
|
||||||
|
for(final optionRaw in raw["options"])
|
||||||
|
CommandOption._new(optionRaw as Map<String, dynamic>)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue