LoginSignup
1
2

More than 3 years have passed since last update.

LaravelでSlackのSlash Commandを複数使う

Last updated at Posted at 2019-03-01

はじめに

前回の記事で、Laravel Slack Slash Commandの大まかな説明をした。
インストールや簡単な使い方などを説明しているので、読んでいない人はそちらを先に読んでほしい。
今回は、複数のコマンドを扱う方法について説明する。

今回の目標

スクリーンショット 2019-03-01 20.05.49.png
と送信すると、
スクリーンショット 2019-03-01 20.08.55.png
と応答してくれる。


スクリーンショット 2019-03-01 20.06.31.png
と送信すると、
スクリーンショット 2019-03-01 20.06.43.png
と応答してくれる。

Slack側の設定

スクリーンショット 2019-03-01 20.11.52.png
こんな感じで、2つのコマンド/hello/how-are-you?を用意する。

/hello

スクリーンショット 2019-03-01 20.20.32.png

/how-are-you?

スクリーンショット 2019-03-01 20.20.50.png

2つのコマンドで、1つのURLを共有している。

Laravel側の設定

今回の一番悩ましいところは、2つのトークンをどう記述するかだ。
トークンは前回同様、config/laravel-slash-command.phpに記述する。
結論から言うと、トークンは配列で記述すると良い。

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
を実行すると、その中に
スクリーンショット 2019-03-01 21.03.49.png
があるように、
/slackにslackからリクエストがPOSTされると、画像にあるコントローラーのメソッドが呼ばれる事がわかる。
Spatieのgithubか、下記のファイルを確認する。

vender/spatie/laravel-slack-slash-command/src/Controller.php
//該当する部分のみを抜き出している
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の書き方

app/SlashCommandHandlers/Hello.php
<?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.");
    }
}
app/SlashCommandHandlers/HowAreYou.php
<?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で条件分岐を行っている感じだ。

おわりに

仕組みを理解できれば、今後簡単にコマンドを増やすことができるだろう。
ご質問があればいつでもどうぞ。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2