Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Commit

Permalink
Merge pull request #46 from nyxx-discord/dev
Browse files Browse the repository at this point in the history
Release 4.2.0
  • Loading branch information
abitofevrything authored Apr 29, 2022
2 parents fb7e7c0 + 595b061 commit 5fa84ea
Show file tree
Hide file tree
Showing 17 changed files with 303 additions and 148 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 4.2.0
__29.04.2022__

- feature: Add support for the permissions v2 system
- deprecations: Deprecated all command permission builders, the `defaultPermissions` and `permissions` fields of `SlashCommandBuilder`, the `bulkOverrideGuildCommandsPermissions` endpoint method and `defaultPermissions` from `SlashCommand`.

## 4.1.0
__09.04.2022__

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ The dartdocs page will always have the documentation for the latest release.

## Contributing to Nyxx

Read [contributing document](https://github.com/l7ssha/nyxx_interactions/blob/development/CONTRIBUTING.md)
Read [contributing document](https://github.com/nyxx-discord/nyxx_interactions/blob/dev/CONTRIBUTING.md)

## Credits

Expand Down
4 changes: 1 addition & 3 deletions example/basic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ final singleCommand = SlashCommandBuilder("help", "This is example help command"
// for main handler because only sub commands will be invokable.
// In list for options you can create new instances of sub commands with
// commands handlers that command could be responded by bot.
final subCommand = SlashCommandBuilder("game", "This is example game command", [
subCommandFlipGame
]);
final subCommand = SlashCommandBuilder("game", "This is example game command", [subCommandFlipGame]);

// Subcommand event handler receives same SlashCommandInteraction parameter with all
// info and tools need to respond to an interaction
Expand Down
1 change: 1 addition & 0 deletions lib/nyxx_interactions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export 'src/models/interaction.dart'
show IComponentInteraction, IInteraction, IButtonInteraction, IMultiselectInteraction, ISlashCommandInteraction, IModalInteraction;
export 'src/models/interaction_data_resolved.dart' show IInteractionDataResolved, IPartialChannel;
export 'src/models/interaction_option.dart' show IInteractionOption;
export 'src/models/slash_command_permission.dart' show ISlashCommandPermissionOverride, ISlashCommandPermissionOverrides, SlashCommandPermissionType;
export 'src/models/slash_command.dart' show ISlashCommand;
export 'src/models/slash_command_type.dart' show SlashCommandType;

Expand Down
3 changes: 3 additions & 0 deletions lib/src/builders/command_permission_builder.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:nyxx/nyxx.dart';

/// Used to define permissions for a particular command.
@Deprecated('Use SlashCommandBuilder.canBeUsedInDm and SlashCommandBuilder.requiresPermissions instead')
abstract class CommandPermissionBuilderAbstract extends Builder {
int get type;

Expand All @@ -20,6 +21,7 @@ abstract class CommandPermissionBuilderAbstract extends Builder {
}

/// A permission for a single role that can be used in [SlashCommandBuilder]
@Deprecated('Use SlashCommandBuilder.canBeUsedInDm and SlashCommandBuilder.requiresPermissions instead')
class RoleCommandPermissionBuilder extends CommandPermissionBuilderAbstract {
@override
late final int type = 1;
Expand All @@ -32,6 +34,7 @@ class RoleCommandPermissionBuilder extends CommandPermissionBuilderAbstract {
}

/// A permission for a single user that can be used in [SlashCommandBuilder]
@Deprecated('Use SlashCommandBuilder.canBeUsedInDm and SlashCommandBuilder.requiresPermissions instead')
class UserCommandPermissionBuilder extends CommandPermissionBuilderAbstract {
@override
late final int type = 2;
Expand Down
29 changes: 26 additions & 3 deletions lib/src/builders/slash_command_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class SlashCommandBuilder extends Builder {
final String? description;

/// If people can use the command by default or if they need permissions to use it.
@Deprecated('Use canBeUsedInDm and requiresPermissions instead')
final bool defaultPermissions;

/// The guild that the slash Command is registered in. This can be null if its a global command.
Expand All @@ -30,6 +31,7 @@ class SlashCommandBuilder extends Builder {
List<CommandOptionBuilder> options;

/// Permission overrides for the command
@Deprecated('Use canBeUsedInDm and requiresPermissions instead')
List<CommandPermissionBuilderAbstract>? permissions;

/// Target of slash command if different that SlashCommandTarget.chat - slash command will
Expand All @@ -39,9 +41,27 @@ class SlashCommandBuilder extends Builder {
/// Handler for SlashCommandBuilder
SlashCommandHandler? handler;

/// Whether this slash command can be used in a DM channel with the bot.
final bool canBeUsedInDm;

/// A set of permissions required by users in guilds to execute this command.
///
/// The integer to use for a permission can be obtained by using [PermissionsConstants]. If a member has any of the permissions combined with the bitwise OR
/// operator, they will be allowed to execute the command.
int? requiredPermissions;

/// A slash command, can only be instantiated through a method on [Interactions]
SlashCommandBuilder(this.name, this.description, this.options,
{this.defaultPermissions = true, this.permissions, this.guild, this.type = SlashCommandType.chat}) {
SlashCommandBuilder(
this.name,
this.description,
this.options, {
this.canBeUsedInDm = true,
this.requiredPermissions,
this.guild,
this.type = SlashCommandType.chat,
this.defaultPermissions = true,
this.permissions,
}) {
if (!slashCommandNameRegex.hasMatch(name)) {
throw ArgumentError("Command name has to match regex: ${slashCommandNameRegex.pattern}");
}
Expand All @@ -59,16 +79,19 @@ class SlashCommandBuilder extends Builder {
RawApiMap build() => {
"name": name,
if (type == SlashCommandType.chat) "description": description,
"default_permission": defaultPermissions,
if (options.isNotEmpty) "options": options.map((e) => e.build()).toList(),
"type": type.value,
"dm_permission": canBeUsedInDm,
if (requiredPermissions != null) "default_member_permissions": requiredPermissions.toString(),
"default_permission": defaultPermissions,
};

void setId(Snowflake id) => _id = id;

Snowflake get id => _id;

/// Register a permission
@Deprecated('Use canBeUsedInDm and requiresPermissions instead')
void addPermission(CommandPermissionBuilderAbstract permission) {
permissions ??= [];

Expand Down
22 changes: 20 additions & 2 deletions lib/src/interactions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:nyxx_interactions/src/internal/sync/commands_sync.dart';
import 'package:nyxx_interactions/src/internal/sync/manual_command_sync.dart';
import 'package:nyxx_interactions/src/internal/utils.dart';
import 'package:nyxx_interactions/src/models/command_option.dart';
import 'package:nyxx_interactions/src/models/slash_command_permission.dart';
import 'package:nyxx_interactions/src/typedefs.dart';
import 'package:nyxx_interactions/src/events/interaction_event.dart';
import 'package:nyxx_interactions/src/builders/command_option_builder.dart';
Expand Down Expand Up @@ -76,6 +77,7 @@ abstract class IInteractions {
/// Interaction extension for Nyxx. Allows use of: Slash Commands.
class Interactions implements IInteractions {
static const _interactionCreateCommand = "INTERACTION_CREATE";
static const _commandPermissionsUpdate = "APPLICATION_COMMAND_PERMISSIONS_UPDATE";

final Logger _logger = Logger("Interactions");
final _commandBuilders = <SlashCommandBuilder>[];
Expand All @@ -85,6 +87,8 @@ class Interactions implements IInteractions {
final _autocompleteHandlers = <String, AutocompleteInteractionHandler>{};
final _multiselectHandlers = <String, MultiselectInteractionHandler>{};

final permissionOverridesCache = <Snowflake, Map<Snowflake, SlashCommandPermissionOverrides>>{};

@override
late final IEventController events;

Expand All @@ -106,7 +110,7 @@ class Interactions implements IInteractions {
/// Create new instance of the interactions class.
Interactions(this.backend) {
events = EventController();
interactionsEndpoints = InteractionsEndpoints(client);
interactionsEndpoints = InteractionsEndpoints(client, this);

backend.setup();

Expand Down Expand Up @@ -145,6 +149,14 @@ class Interactions implements IInteractions {
default:
_logger.warning("Unknown interaction type: [$type]; Payload: ${jsonEncode(rawData)}");
}
} else if (rawData["op"] == 0 && rawData["t"] == _commandPermissionsUpdate) {
final overrides = SlashCommandPermissionOverrides(rawData["d"] as RawApiMap, client);

Snowflake guildId = Snowflake(rawData["d"]["guild_id"]);
Snowflake commandId = Snowflake(rawData["d"]["id"]);

permissionOverridesCache[guildId] ??= {};
permissionOverridesCache[guildId]![commandId] = overrides;
}
});
}
Expand Down Expand Up @@ -190,7 +202,13 @@ class Interactions implements IInteractions {
_commands.add(command);
}

await interactionsEndpoints.bulkOverrideGuildCommandsPermissions(client.appId, entry.key, entry.value);
if (entry.value.any((element) => element.permissions?.isNotEmpty ?? false)) {
_logger.warning(
'Using deprecated permissions endpoint. To fix, use SlashCommandBuilder.canBeUsedInDm and SlashCommandBuilder.requiresPermissions'
' instead of SlashCommandBuilder.permissions',
);
await interactionsEndpoints.bulkOverrideGuildCommandsPermissions(client.appId, entry.key, entry.value);
}
}

for (final globalCommandBuilder in entry.value) {
Expand Down
46 changes: 34 additions & 12 deletions lib/src/internal/interaction_endpoints.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'package:nyxx/nyxx.dart';
import 'package:nyxx/src/core/message/message.dart';
import 'package:nyxx_interactions/src/builders/modal_builder.dart';
import 'package:nyxx_interactions/nyxx_interactions.dart';
import 'package:nyxx_interactions/src/interactions.dart';

import 'package:nyxx_interactions/src/models/slash_command.dart';
import 'package:nyxx_interactions/src/builders/slash_command_builder.dart';
import 'package:nyxx_interactions/src/builders/arg_choice_builder.dart';
import 'package:nyxx_interactions/src/models/slash_command_permission.dart';

abstract class IInteractionsEndpoints {
/// Sends followup for interaction with given [token]. IMessage will be created with [builder]
Expand Down Expand Up @@ -73,16 +73,22 @@ abstract class IInteractionsEndpoints {
Stream<ISlashCommand> bulkOverrideGuildCommands(Snowflake applicationId, Snowflake guildId, Iterable<SlashCommandBuilder> builders);

/// Overrides permissions for guild commands
@Deprecated("This endpoint requires OAuth2 authentication, which nyxx_interactions doesn't support."
" Use SlashCommandBuilder.canBeUsedInDm and SlashCommandBuilder.requiresPermissions instead.")
Future<void> bulkOverrideGuildCommandsPermissions(Snowflake applicationId, Snowflake guildId, Iterable<SlashCommandBuilder> builders);

/// Responds to autocomplete interaction
Future<void> respondToAutocomplete(Snowflake interactionId, String token, List<ArgChoiceBuilder> builders);

/// Fetch the command permission overrides for a command in a guild.
Future<ISlashCommandPermissionOverrides> fetchCommandOverrides(Snowflake commandId, Snowflake guildId);
}

class InteractionsEndpoints implements IInteractionsEndpoints {
final INyxx _client;
final Interactions _interactions;

InteractionsEndpoints(this._client);
InteractionsEndpoints(this._client, this._interactions);

@override
Future<void> acknowledge(String token, String interactionId, bool hidden, int opCode) async {
Expand Down Expand Up @@ -200,7 +206,7 @@ class InteractionsEndpoints implements IInteractionsEndpoints {
}

for (final rawRes in (response as IHttpResponseSucess).jsonBody as List<dynamic>) {
yield SlashCommand(rawRes as RawApiMap, _client);
yield SlashCommand(rawRes as RawApiMap, _interactions);
}
}

Expand All @@ -213,7 +219,7 @@ class InteractionsEndpoints implements IInteractionsEndpoints {
}

for (final rawRes in (response as IHttpResponseSucess).jsonBody as List<dynamic>) {
yield SlashCommand(rawRes as RawApiMap, _client);
yield SlashCommand(rawRes as RawApiMap, _interactions);
}
}

Expand Down Expand Up @@ -243,7 +249,7 @@ class InteractionsEndpoints implements IInteractionsEndpoints {
return Future.error(response);
}

return SlashCommand((response as IHttpResponseSucess).jsonBody as RawApiMap, _client);
return SlashCommand((response as IHttpResponseSucess).jsonBody as RawApiMap, _interactions);
}

@override
Expand All @@ -255,7 +261,7 @@ class InteractionsEndpoints implements IInteractionsEndpoints {
return Future.error(response);
}

return SlashCommand((response as IHttpResponseSucess).jsonBody as RawApiMap, _client);
return SlashCommand((response as IHttpResponseSucess).jsonBody as RawApiMap, _interactions);
}

@override
Expand All @@ -266,7 +272,7 @@ class InteractionsEndpoints implements IInteractionsEndpoints {
return Future.error(response);
}

return SlashCommand((response as IHttpResponseSucess).jsonBody as RawApiMap, _client);
return SlashCommand((response as IHttpResponseSucess).jsonBody as RawApiMap, _interactions);
}

@override
Expand All @@ -278,7 +284,7 @@ class InteractionsEndpoints implements IInteractionsEndpoints {
}

for (final commandSlash in (response as IHttpResponseSucess).jsonBody as List<dynamic>) {
yield SlashCommand(commandSlash as RawApiMap, _client);
yield SlashCommand(commandSlash as RawApiMap, _interactions);
}
}

Expand All @@ -290,7 +296,7 @@ class InteractionsEndpoints implements IInteractionsEndpoints {
return Future.error(response);
}

return SlashCommand((response as IHttpResponseSucess).jsonBody as RawApiMap, _client);
return SlashCommand((response as IHttpResponseSucess).jsonBody as RawApiMap, _interactions);
}

@override
Expand All @@ -302,10 +308,12 @@ class InteractionsEndpoints implements IInteractionsEndpoints {
}

for (final commandSlash in (response as IHttpResponseSucess).jsonBody as List<dynamic>) {
yield SlashCommand(commandSlash as RawApiMap, _client);
yield SlashCommand(commandSlash as RawApiMap, _interactions);
}
}

@Deprecated("This endpoint requires OAuth2 authentication, which nyxx_interactions doesn't support."
" Use SlashCommandBuilder.canBeUsedInDm and SlashCommandBuilder.requiresPermissions instead.")
Future<void> bulkOverrideGlobalCommandsPermissions(Snowflake applicationId, Iterable<SlashCommandBuilder> builders) async {
final globalBody = builders
.where((builder) => builder.permissions != null && builder.permissions!.isNotEmpty)
Expand All @@ -319,6 +327,8 @@ class InteractionsEndpoints implements IInteractionsEndpoints {
}

@override
@Deprecated("This endpoint requires OAuth2 authentication, which nyxx_interactions doesn't support."
" Use SlashCommandBuilder.canBeUsedInDm and SlashCommandBuilder.requiresPermissions instead.")
Future<void> bulkOverrideGuildCommandsPermissions(Snowflake applicationId, Snowflake guildId, Iterable<SlashCommandBuilder> builders) async {
final guildBody = builders
.where((b) => b.permissions != null && b.permissions!.isNotEmpty)
Expand Down Expand Up @@ -367,4 +377,16 @@ class InteractionsEndpoints implements IInteractionsEndpoints {
return Future.error(response);
}
}

/// Fetch the command permission overrides for a command in a guild.
@override
Future<SlashCommandPermissionOverrides> fetchCommandOverrides(Snowflake commandId, Snowflake guildId) async {
final response = await _client.httpEndpoints.sendRawRequest("/applications/${_client.appId}/guilds/$guildId/commands/$commandId/permissions", "GET");

if (response is IHttpResponseError) {
return Future.error(response);
}

return SlashCommandPermissionOverrides((response as IHttpResponseSucess).jsonBody as Map<String, dynamic>, _client);
}
}
Loading

0 comments on commit 5fa84ea

Please sign in to comment.