spatie/laravel-slack-slash-command
Build Slack slash commands in Laravel. Define handlers to validate and process incoming Slack requests, reply within 3 seconds or dispatch jobs for longer work, and send structured responses back to Slack. Includes request/response helpers and simple routing of commands.
Installation:
composer require spatie/laravel-slack-slash-command
Publish the config file:
php artisan vendor:publish --provider="Spatie\SlackSlashCommand\SlackSlashCommandServiceProvider"
Configure Slack:
/mycommand).https://your-app.test/slack/mycommand)..env:
SLACK_SIGNING_SECRET=your_signing_secret_here
Define a Command Handler:
Create a handler class (e.g., app/Slack/Commands/MyCommandHandler.php):
namespace App\Slack\Commands;
use Spatie\SlackSlashCommand\SlackCommand;
class MyCommandHandler
{
public function handle(SlackCommand $slackCommand): string
{
return "Hello, {$slackCommand->user->name}! Your input: {$slackCommand->body('text')}";
}
}
Register the Handler:
Bind the handler in AppServiceProvider:
public function boot()
{
SlackCommand::macro('mycommand', function () {
return new \App\Slack\Commands\MyCommandHandler();
});
}
Route the Command:
Add a route in routes/web.php:
use Spatie\SlackSlashCommand\SlackCommand;
Route::post('/slack/mycommand', function () {
return SlackCommand::mycommand()->handle();
});
Test Locally: Use Slack's Socket Mode or tools like ngrok to test locally.
Create a /greet command that responds with a personalized message:
// app/Slack/Commands/GreetHandler.php
public function handle(SlackCommand $slackCommand): string
{
$name = $slackCommand->body('text') ?: $slackCommand->user->name;
return "Hi {$name}! Welcome to our app. 🎉";
}
/greet @user or /greet John.Validate Input:
public function handle(SlackCommand $slackCommand): string
{
$text = $slackCommand->body('text');
if (empty($text)) {
return "Please provide a task name. Example: `/todo Add laundry`";
}
// ...
}
Interact with Laravel Services: Inject dependencies (e.g., repositories, services) into the handler:
use App\Services\TodoService;
public function __construct(private TodoService $todoService) {}
public function handle(SlackCommand $slackCommand): string
{
$task = $this->todoService->create($slackCommand->body('text'));
return "Added task: *{$task->name}* (ID: {$task->id})";
}
Attachments and Interactive Responses: Return rich messages with attachments:
public function handle(SlackCommand $slackCommand): string
{
return response()->json([
'text' => 'Your task was created!',
'attachments' => [
[
'title' => 'Task Details',
'fields' => [
['title' => 'Name', 'value' => $task->name, 'short' => true],
['title' => 'Due', 'value' => $task->due_date, 'short' => true],
],
],
],
]);
}
Error Handling: Use Laravel's exception handling or return user-friendly errors:
try {
$this->todoService->create($text);
} catch (\Exception $e) {
return "❌ Error: {$e->getMessage()}";
}
Authentication: Verify the Slack user or team before processing:
if ($slackCommand->team->id !== config('slack.allowed_team_id')) {
return "This command is restricted to our team.";
}
Rate Limiting:
Use Laravel's throttle middleware to limit command frequency:
Route::post('/slack/todo', function () {
return SlackCommand::todo()->handle();
})->middleware('throttle:5,1'); // 5 requests per minute
Logging: Log command executions for debugging:
\Log::info('Slack command executed', [
'command' => $slackCommand->command,
'user' => $slackCommand->user->name,
'input' => $slackCommand->body(),
]);
Testing:
Use SlackCommand facade in tests:
public function test_greet_command()
{
$response = $this->post('/slack/greet', [
'text' => 'John',
'token' => 'valid_token',
'team_id' => 'T123',
'user_id' => 'U456',
'user_name' => 'john_doe',
]);
$response->assertJson(['response_type' => 'ephemeral', 'text' => 'Hi John! Welcome...']);
}
Signing Secret Mismatch:
SLACK_SIGNING_SECRET in .env matches Slack's app settings.InvalidSlackRequestException.Missing response_type:
response_type in the JSON response (e.g., in_channel, ephemeral).response_type:
return response()->json(['response_type' => 'in_channel', 'text' => '...']);
CORS Issues:
// config/cors.php
'paths' => ['api/*', 'slack/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://your-slack-app.slack.com'],
Command Not Triggering:
php artisan route:list to confirm the route exists.Large Payloads:
Inspect Raw Request:
Dump the $slackCommand->body() to verify incoming data:
\Log::debug('Raw body:', $slackCommand->body()->all());
Enable Slack Debugging: Add this to your handler to log the full request:
\Log::debug('Slack request:', [
'command' => $slackCommand->command,
'body' => $slackCommand->body()->all(),
'user' => $slackCommand->user,
'team' => $slackCommand->team,
]);
Verify Webhook URL: Use Slack's Socket Mode for local testing or tools like RequestBin to inspect incoming requests.
Custom Validation:
Extend the SlackCommand class to add validation rules:
use Spatie\SlackSlashCommand\SlackCommand as BaseSlackCommand;
class SlackCommand extends BaseSlackCommand
{
public function validate(array $rules)
{
return validator($this->body->all(), $rules);
}
}
Middleware: Add middleware to the route for pre-processing:
Route::post('/slack/admin-command', function () {
return SlackCommand::admin()->handle();
})->middleware(\App\Http\Middleware\CheckAdmin::class);
Dynamic Command Registration: Register commands dynamically based on user roles or config:
if (config('slack.enable_todo_command')) {
SlackCommand::macro('todo', function () {
return new \App\Slack\Commands\Todo
How can I help you explore Laravel packages today?