Skip to content

Commit

Permalink
✨ Add support for Context Menus (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
NinjaLabs-Dev authored Jul 18, 2024
1 parent cee289e commit 92b1527
Show file tree
Hide file tree
Showing 12 changed files with 563 additions and 115 deletions.
15 changes: 15 additions & 0 deletions config/discord.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,21 @@
Laracord\Commands\HelpCommand::class,
],

/*
|--------------------------------------------------------------------------
| Additional Context Menus
|--------------------------------------------------------------------------
|
| Here you may specify any additional context menus for the Discord bot.
| These context menus will be loaded in addition to the context menus
| automatically loaded in your project.
|
*/

'menus' => [
//
],

/*
|--------------------------------------------------------------------------
| Additional Services
Expand Down
31 changes: 30 additions & 1 deletion src/Commands/AbstractCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Laracord\Commands;

use Discord\Parts\Guild\Guild;
use Discord\Parts\Interactions\Command\Command;
use Discord\Parts\User\User;
use Illuminate\Support\Str;
use Laracord\Discord\Concerns\HasModal;
Expand Down Expand Up @@ -54,13 +55,23 @@ abstract class AbstractCommand
*/
protected $description;

/**
* The command type.
*/
protected string|int $type = 'chat';

/**
* The guild the command belongs to.
*
* @var string
*/
protected $guild;

/**
* Determine whether the command can be used in a direct message.
*/
protected bool $directMessage = true;

/**
* Determines whether the command requires admin permissions.
*
Expand Down Expand Up @@ -140,6 +151,14 @@ public function isAdmin($user)
return $this->getUser($user)->is_admin;
}

/**
* Determine if the command can be used in a direct message.
*/
public function canDirectMessage(): bool
{
return $this->directMessage;
}

/**
* Resolve a Discord user.
*/
Expand Down Expand Up @@ -189,7 +208,7 @@ public function getName()
*/
public function getSignature()
{
return Str::start($this->getName(), $this->bot()->getPrefix());
return $this->getName();
}

/**
Expand Down Expand Up @@ -218,6 +237,16 @@ public function getDescription()
return $this->description;
}

/**
* Get the command type.
*/
public function getType(): int
{
return match ($this->type) {
default => Command::CHAT_INPUT,
};
}

/**
* Retrieve the command guild.
*/
Expand Down
42 changes: 42 additions & 0 deletions src/Commands/ApplicationCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Laracord\Commands;

abstract class ApplicationCommand extends AbstractCommand
{
/**
* The permissions required to use the command.
*
* @var array
*/
protected $permissions = [];

/**
* Determine if the command is not safe for work.
*/
protected bool $nsfw = false;

/**
* Retrieve the slash command bitwise permission.
*/
public function getPermissions(): ?string
{
if (! $this->permissions) {
return null;
}

$permissions = collect($this->permissions)
->mapWithKeys(fn ($permission) => [$permission => true])
->all();

return (new RolePermission($this->discord(), $permissions))->__toString();
}

/**
* Determine if the command is not safe for work.
*/
public function isNsfw(): bool
{
return $this->nsfw;
}
}
15 changes: 15 additions & 0 deletions src/Commands/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Laracord\Commands;

use Illuminate\Support\Str;
use Laracord\Commands\Contracts\Command as CommandContract;

abstract class Command extends AbstractCommand implements CommandContract
Expand Down Expand Up @@ -43,6 +44,10 @@ abstract class Command extends AbstractCommand implements CommandContract
*/
public function maybeHandle($message, $args)
{
if (! $this->canDirectMessage() && ! $message->guild_id) {
return;
}

if ($this->getGuild() && $message->guild_id !== $this->getGuild()) {
return;
}
Expand Down Expand Up @@ -89,6 +94,16 @@ public function getCooldownMessage()
return $this->cooldownMessage;
}

/**
* Retrieve the command signature.
*
* @return string
*/
public function getSignature()
{
return Str::start($this->getName(), $this->bot()->getPrefix());
}

/**
* Retrieve the command usage.
*
Expand Down
80 changes: 80 additions & 0 deletions src/Commands/ContextMenu.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

namespace Laracord\Commands;

use Discord\Parts\Interactions\Command\Command as DiscordCommand;
use Laracord\Commands\Contracts\ContextMenu as ContextMenuContract;

abstract class ContextMenu extends ApplicationCommand implements ContextMenuContract
{
/**
* The context menu type.
*/
protected string|int $type = 'message';

/**
* Create a Discord command instance.
*/
public function create(): DiscordCommand
{
$menu = collect([
'name' => $this->getName(),
'type' => $this->getType(),
'guild_id' => $this->getGuild(),
'default_member_permissions' => $this->getPermissions(),
'default_permission' => true,
'dm_permission' => $this->canDirectMessage(),
'nsfw' => $this->isNsfw(),
])->reject(fn ($value) => blank($value));

return new DiscordCommand($this->discord(), $menu->all());
}

/**
* Handle the context menu interaction.
*
* @param \Discord\Parts\Interactions\Interaction $interaction
* @return void
*/
abstract public function handle($interaction);

/**
* Maybe handle the context menu interaction.
*
* @param \Discord\Parts\Interactions\Interaction $interaction
* @return void
*/
public function maybeHandle($interaction)
{
if (! $this->isAdminCommand()) {
$this->handle($interaction);

return;
}

if ($this->isAdminCommand() && ! $this->isAdmin($interaction->member->user)) {
return $interaction->respondWithMessage(
$this
->message('You do not have permission to run this command.')
->title('Permission Denied')
->error()
->build(),
ephemeral: true
);
}

$this->handle($interaction);
}

/**
* Get the context menu type.
*/
public function getType(): int
{
return match ($this->type) {
'user' => DiscordCommand::USER,
DiscordCommand::USER => DiscordCommand::USER,
default => DiscordCommand::MESSAGE,
};
}
}
13 changes: 13 additions & 0 deletions src/Commands/Contracts/ContextMenu.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Laracord\Commands\Contracts;

use Discord\Parts\Interactions\Interaction;

interface ContextMenu
{
/**
* Handle the context menu interaction.
*/
public function handle(Interaction $interaction);
}
31 changes: 5 additions & 26 deletions src/Commands/SlashCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,12 @@
use Discord\Parts\Interactions\Command\Command as DiscordCommand;
use Discord\Parts\Interactions\Command\Option;
use Discord\Parts\Interactions\Interaction;
use Discord\Parts\Permissions\RolePermission;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Laracord\Commands\Contracts\SlashCommand as SlashCommandContract;

abstract class SlashCommand extends AbstractCommand implements SlashCommandContract
abstract class SlashCommand extends ApplicationCommand implements SlashCommandContract
{
/**
* The permissions required to use the command.
*
* @var array
*/
protected $permissions = [];

/**
* The command options.
*
Expand Down Expand Up @@ -50,7 +42,10 @@ public function create(): DiscordCommand
{
$command = CommandBuilder::new()
->setName($this->getName())
->setDescription($this->getDescription());
->setDescription($this->getDescription())
->setType($this->getType())
->setDmPermission($this->canDirectMessage())
->setNsfw($this->isNsfw());

if ($permissions = $this->getPermissions()) {
$command = $command->setDefaultMemberPermissions($permissions);
Expand Down Expand Up @@ -243,22 +238,6 @@ public function getSignature()
return Str::start($this->getName(), '/');
}

/**
* Retrieve the slash command bitwise permission.
*/
public function getPermissions(): ?string
{
if (! $this->permissions) {
return null;
}

$permissions = collect($this->permissions)
->mapWithKeys(fn ($permission) => [$permission => true])
->all();

return (new RolePermission($this->discord(), $permissions))->__toString();
}

/**
* Retrieve the slash command options.
*/
Expand Down
Loading

0 comments on commit 92b1527

Please sign in to comment.