plytas / laravel-discord-interactions
使用 Discord HTTP API 创建和响应交互的 Laravel (PHP) 客户端
Requires
- php: ^8.2
- illuminate/contracts: ^10.0||^11.0
- spatie/laravel-data: ^4.6
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^2.9
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^9.0.0||^8.22.0
- pestphp/pest: ^2.34
- pestphp/pest-plugin-arch: ^2.7
- pestphp/pest-plugin-laravel: ^2.3
- phpstan/extension-installer: ^1.3
- phpstan/phpstan-deprecation-rules: ^1.1
- phpstan/phpstan-phpunit: ^1.3
- spatie/laravel-ray: ^1.35
README
使用 Laravel 创建丰富的 Discord 交互并对其做出响应。利用 Discord HTTP webhooks,无需长时间运行进程。
安装
您可以通过 composer 安装此包
composer require plytas/laravel-discord-interactions
您可以使用以下命令发布配置文件:
php artisan vendor:publish --tag="discord-interactions-config"
这是发布配置文件的内容
return [ 'application_id' => env('DISCORD_APPLICATION_ID'), 'public_key' => env('DISCORD_PUBLIC_KEY'), 'bot_token' => env('DISCORD_BOT_TOKEN'), 'route' => [ 'path' => env('DISCORD_ROUTE', '/discord'), 'middleware' => [ 'before' => [ //ThrottleRequests::class, ], 'after' => [ ], ], ], ];
配置
在 https://discord.com/developers/applications 上创建一个新的 Discord 应用程序。从“通用信息”选项卡复制应用程序 ID 和公钥,从“机器人”选项卡复制机器人令牌,并将其添加到您的 .env
文件中。
交互端点设置
对于本地开发,您可以使用例如 expose 或 ngrok 这样的服务将您的本地服务器公开到互联网。
在您的 Discord 应用程序中,在“通用信息”下填写“交互端点 URL”,使用您服务器的 URL,后跟配置文件中设置的路径。默认情况下为 /discord
。
https://your-server-url.com/discord
点击“保存更改”按钮时,Discord 将向提供的 URL 发送几个 POST
请求以验证端点。其中之一将故意包含无效签名。该包将自动对这些请求做出适当的响应。
如果更改成功保存,您就可以开始使用了!
用法
创建命令
要创建新的聊天命令,创建一个新的类,该类实现了 Plytas\Discord\Contracts\DiscordCommand
接口。
use Plytas\Discord\Contracts\DiscordCommand; use Plytas\Discord\Data\DiscordInteraction; use Plytas\Discord\Data\DiscordMessage; use Plytas\Discord\Data\DiscordResponse; class PingCommand implements DiscordCommand { public function description(): string { return 'Replies with pong'; } public function handle(DiscordInteraction $interaction): DiscordResponse { return $interaction->respondWithMessage( DiscordMessage::new() ->setContent('Pong!') ->ephemeral() ); } }
注册命令
您可以使用 Plytas\Discord\DiscordCommandRegistry::setCommands
方法注册命令。这可以在服务提供者的 boot()
方法中完成。
use Plytas\Discord\DiscordCommandRegistry; DiscordCommandRegistry::setCommands([ 'ping' => PingCommand::class, // Nested commands 'user' => [ 'role' => [ 'add' => AddRoleCommand::class, 'remove' => RemoveRoleCommand::class, ], ], ]);
最后调用 php artisan discord:register-commands
以将命令注册到 Discord。建议在部署脚本中运行此命令。
响应交互
您的命令将接收一个包含有关交互信息的 DiscordInteraction
对象。您可以通过直接返回一个 DiscordResponse
对象来直接响应交互。DiscordInteraction
对象包含创建响应的辅助方法,例如 respondWithMessage()
、updateMessage()
和 showModal()
。
您必须在 3 秒内对交互做出响应。如果您未能这样做,Discord 将向用户显示一个通用错误消息。如果您需要更多时间来处理交互,建议您返回一条消息,表明命令仍在处理中。完成后,您可以在 \Illuminate\Support\Facades\App::terminating()
回调中使用 \Plytas\Discord\Facades\Discord::updateInteractionMessage()
来更新消息。
use Illuminate\Support\Facades\App; use Plytas\Discord\Contracts\DiscordCommand; use Plytas\Discord\Data\DiscordInteraction; use Plytas\Discord\Data\DiscordMessage; use Plytas\Discord\Data\DiscordResponse; use Plytas\Discord\Facades\Discord; class PingCommand implements DiscordCommand { public function description(): string { return 'Replies with pong'; } public function handle(DiscordInteraction $interaction): DiscordResponse { // Register a terminating callback to update the message after processing is done App::terminating(function () use ($interaction) { // Simulate a long-running process sleep(5); Discord::updateInteractionMessage( interaction: $interaction, message: DiscordMessage::new() ->setContent('Pong!') ->ephemeral() ); }); // Respond with a message that indicates that the command is still processing return $interaction->respondWithMessage( DiscordMessage::new() ->setContent('Calculating...') ->ephemeral() // Only the user who invoked the command can see the message ); } }
响应组件交互
您可以在消息中添加组件,以便用户与之交互。
use Plytas\Discord\Components\ActionRow; use Plytas\Discord\Components\Button; use Plytas\Discord\Contracts\DiscordCommand; use Plytas\Discord\Data\DiscordInteraction; use Plytas\Discord\Data\DiscordMessage; use Plytas\Discord\Data\DiscordResponse; use Plytas\Discord\Enums\ButtonStyle; class RockPaperScissorsCommand implements DiscordCommand { public function description(): string { return 'Play rock-paper-scissors'; } public function handle(DiscordInteraction $interaction): DiscordResponse { return $interaction->respondWithMessage( DiscordMessage::new() ->setContent('Select your move') ->addComponent( component: ActionRow::new() ->addComponent( component: Button::new() ->setCustomId('rock') ->setLabel('Rock') ->setEmoji('🪨') ->setStyle(ButtonStyle::Primary) ) ->addComponent( component: Button::new() ->setCustomId('paper') ->setLabel('Paper') ->setEmoji('📄') ->setStyle(ButtonStyle::Primary) ) ->addComponent( component: Button::new() ->setCustomId('scissors') ->setLabel('Scissors') ->setEmoji('✂️') ->setStyle(ButtonStyle::Primary) ) ) ->ephemeral() // Only the user who invoked the command can see the message ); } }
您可以通过创建一个新的类并实现 \Plytas\Discord\Contracts\DiscordComponentHandler
接口来响应组件交互。
use Illuminate\Support\Arr; use Plytas\Discord\Contracts\DiscordComponentHandler; use Plytas\Discord\Data\DiscordInteraction; use Plytas\Discord\Data\DiscordMessage; use Plytas\Discord\Data\DiscordResponse; class RockPaperScissorsComponentHandler implements DiscordComponentHandler { public function handle(DiscordInteraction $interaction): DiscordResponse { $playerSelection = $interaction->getMessageComponent()->custom_id; $botSelection = Arr::random(['rock', 'paper', 'scissors']); if ($playerSelection === $botSelection) { $message = 'It\'s a tie!'; } $winningMoves = [ 'rock' => 'scissors', 'paper' => 'rock', 'scissors' => 'paper', ]; if ($winningMoves[$playerSelection] === $botSelection) { $message = 'You win!'; } else { $message = 'You lose!'; } return $interaction->updateMessage( DiscordMessage::new() ->setContent($message) ->ephemeral() ); } }
注册组件处理器
您可以使用 Plytas\Discord\DiscordComponentRegistry::setComponentHandlers()
方法注册组件处理器。此方法接受一个 component_id => DiscordComponentHandler class
的数组。这可以在服务提供者的 boot()
方法中完成。
use Plytas\Discord\DiscordComponentRegistry; DiscordComponentRegistry::setComponentHandlers([ 'rock' => RockPaperScissorsComponentHandler::class, 'paper' => RockPaperScissorsComponentHandler::class, 'scissors' => RockPaperScissorsComponentHandler::class, ]);
如果您需要更多控制组件处理器注册过程,可以使用 Plytas\Discord\DiscordComponentRegistry::handleComponentsUsing()
方法。此方法接受一个闭包,该闭包接收 Plytas\Discord\Data\DiscordInteraction
对象,并期望返回一个 Plytas\Discord\Data\DiscordResponse
对象。
想象一下,您的组件具有一个动态的自定义ID,其中包含游戏名称。您可以使用以下代码来处理这些组件
use Illuminate\Support\Str; use Plytas\Discord\Data\DiscordInteraction; use Plytas\Discord\Events\ComponentHandlerRegistered; DiscordComponentRegistry::handleComponentsUsing(function(DiscordInteraction $interaction) { $gameName = Str::afterLast($interaction->getMessageComponent()->custom_id, ':'); return match ($gameName) { 'rock-paper-scissors' => (new RockPaperScissorsComponentHandler)->handle($interaction), 'coin-flip' => (new CoinFlipComponentHandler)->handle($interaction), 'default' => $interaction->updateMessage( DiscordMessage::new() ->setContent('Invalid game') ->ephemeral() ); }; });
警告
并非所有组件和API功能都得到支持。此包最初是一个个人项目,仍在开发中。如果您需要特定的功能,请随时提出问题或发起拉取请求。
测试
composer test
变更日志
有关最近更改的更多信息,请参阅变更日志
贡献
有关详细信息,请参阅贡献指南
安全漏洞
有关如何报告安全漏洞的详细信息,请参阅我们的安全策略
致谢
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件