はじめに
前回の記事で、Laravel Slack Slash Commandの大まかな説明をした。
インストールや簡単な使い方などを説明しているので、読んでいない人はそちらを先に読んでほしい。
今回は、複数のコマンドを扱う方法について説明する。
今回の目標
Slack側の設定
こんな感じで、2つのコマンド/hello
と/how-are-you?
を用意する。
/hello
/how-are-you?
2つのコマンドで、1つのURLを共有している。
Laravel側の設定
今回の一番悩ましいところは、2つのトークンをどう記述するかだ。
トークンは前回同様、config/laravel-slash-command.php
に記述する。
結論から言うと、トークンは配列で記述すると良い。
<?php
return [
/*
* At the integration settings over at Slack you can configure the url to which the
* slack commands are posted. Specify the path component of that url here.
*
* For example `http://example.com/slack` you would put `slack` here.
*/
'url' => 'slack',
/*
* The token generated by Slack with which to verify if a incoming slash command request is valid (deprecated).
*/
'token' => ['/helloのトークン', '/how-are-you?のトークン'], //コマンドのトークンを配列で記述する
/*
* The signing_secret generated by Slack with which to verify if a incoming slash command request is valid.
*/
'signing_secret' => env('SLACK_SIGNING_SECRET'),
/*
* Verify requests from slack with signing_secret signature
*/
'verify_with_signing' => false,
/*
* The handlers that will process the slash command. We'll call handlers from top to bottom
* until the first one whose `canHandle` method returns true.
*/
'handlers' => [
//add your own handlers here
App\SlashCommandHandlers\Hello::class, //コマンド/helloの処理を書くクラス
App\SlashCommandHandlers\HowAreYou::class, //コマンド/how-are-you?の処理を書くクラス
//this handler will display instructions on how to use the various commands.
Spatie\SlashCommand\Handlers\Help::class,
//this handler will respond with a `Could not handle command` message.
Spatie\SlashCommand\Handlers\CatchAll::class,
],
];
つまり、今後コマンドを増やしたかったら、そのトークンを配列に入れていけば良いわけだ。
とっても楽で便利である。
前回はenv関数を使っていたが、それだと複数のトークンを扱えないのだ。
なぜ配列で記述できるのか?
$ php artisan route:list
を実行すると、その中に
があるように、
/slackにslackからリクエストがPOSTされると、画像にあるコントローラーのメソッドが呼ばれる事がわかる。
Spatieのgithubか、下記のファイルを確認する。
//該当する部分のみを抜き出している
class Controller extends IlluminateController
{
/** @var \Spatie\SlashCommand\Request */
protected $request;
/** @var \Illuminate\Support\Collection */
protected $config;
public function __construct(IlluminateRequest $request, Repository $config)
{
$this->request = Request::createFromIlluminateRequest($request);
$this->config = collect($config->get('laravel-slack-slash-command'));
}
public function getResponse(IlluminateRequest $request): IlluminateResponse
{
$this->guardAgainstInvalidRequest($request);
$handler = $this->determineHandler();
try {
if ($handler instanceof SignatureHandler) {
$handler->validate();
}
$response = $handler->handle($this->request);
} catch (SlackSlashCommandException $exception) {
$response = $exception->getResponse($this->request);
} catch (Exception $exception) {
$response = $this->convertToResponse($exception);
}
return $response->getIlluminateResponse();
}
protected function guardAgainstInvalidRequest(IlluminateRequest $request)
{
if ($this->config->get('verify_with_signing')) {
$this->verifyWithSigning($request);
} else {
$this->verifyWithToken($request);
}
}
protected function verifyWithSigning(IlluminateRequest $request)
{
$signature = app(RequestSignature::class)->create($request);
if ($request->header('X-Slack-Signature') !== $signature) {
throw InvalidRequest::invalidSignature($signature);
}
}
protected function verifyWithToken(IlluminateRequest $request)
{
if (! $request->has('token')) {
throw InvalidRequest::tokenNotFound();
}
$validTokens = $this->config->get('token');
if (! is_array($validTokens)) {
$validTokens = [$validTokens];
}
if (! in_array($this->request->get('token'), $validTokens)) {
throw InvalidRequest::invalidToken($this->request->get('token'));
}
}
いま、
$this->config->get('token')
→ config/laravel-slack-slash-command
に書いたトークン
$this->request->get('token')
→ slack command の設定時に、slack側から発行されたトークン
となっている。
関数verifyWithToken
では、$this->config->get('token')
を$validTokens
とした上で、
if (! is_array($validTokens)) {
$validTokens = [$validTokens];
}
$validTokens
が配列じゃないなら配列にするという処理を行っている。
その後、
if (! in_array($this->request->get('token'), $validTokens)) {
throw InvalidRequest::invalidToken($this->request->get('token'));
}
$this->request->get('token')
が配列$validTokens
の中に存在するかを調べている。
つまり、config/laravel-slack-slash-command
に記述するトークンは、最初から配列でも構わない。
というか、こんな処理を行うなら、配列で書くように示してくれれば良いのに…。
ちなみにだが、関数getResponse
内の
$this->guardAgainstInvalidRequest($request);
を消してしまえば、トークンの照合を行わないのだ。
そんなことしちゃだめだぞ。絶対だからな。絶対だぞ。
classの書き方
<?php
namespace App\SlashCommandHandlers;
use Spatie\SlashCommand\Request;
use Spatie\SlashCommand\Response;
use Spatie\SlashCommand\Handlers\BaseHandler;
class Hello extends BaseHandler
{
/**
* If this function returns true, the handle method will get called.
*
* @param \Spatie\SlashCommand\Request $request
*
* @return bool
*/
public function canHandle(Request $request): bool
{
return $request->command == 'hello';
}
/**
* Handle the given request. Remember that Slack expects a response
* within three seconds after the slash command was issued. If
* there is more time needed, dispatch a job.
*
* @param \Spatie\SlashCommand\Request $request
*
* @return \Spatie\SlashCommand\Response
*/
public function handle(Request $request): Response
{
return $this->respondToSlack("Hello! I'm Laravel Slack Slash Command.");
}
}
<?php
namespace App\SlashCommandHandlers;
use Spatie\SlashCommand\Request;
use Spatie\SlashCommand\Response;
use Spatie\SlashCommand\Handlers\BaseHandler;
class HowAreYou extends BaseHandler
{
/**
* If this function returns true, the handle method will get called.
*
* @param \Spatie\SlashCommand\Request $request
*
* @return bool
*/
public function canHandle(Request $request): bool
{
return $request->command == 'how-are-you?';
}
/**
* Handle the given request. Remember that Slack expects a response
* within three seconds after the slash command was issued. If
* there is more time needed, dispatch a job.
*
* @param \Spatie\SlashCommand\Request $request
*
* @return \Spatie\SlashCommand\Response
*/
public function handle(Request $request): Response
{
return $this->respondToSlack("I'm good!");
}
}
クラスの細かい解説は前回の記事に詳しく書いたので省略する。
関数canHandle
で条件分岐を行っている感じだ。
おわりに
仕組みを理解できれば、今後簡単にコマンドを増やすことができるだろう。
ご質問があればいつでもどうぞ。