📝

laravelからslack通知

に公開

重要な概念

slack通知には以下2種類ある

  1. webhook url通知
  2. slack bot通知

ここではwebhook urlのみ取り上げる。

ドキュメントやらライブラリーの刷新などによりインターネットの海から拾ってきた情報をそのまま適用すると以外と動かないのがslack通知だ。ドキュメントの鮮度が古いのはあんま使われてないのからかもだけど、ちゃんとやると便利とは思うんですがね。

通知先channel作る

ここではaws-costとした。今awsのコスト通知アプリを作っていたからだけど、まあここはお好きに。

appを作る

https://api.slack.com/apps より適当に作る

incoming webhook作る

この辺から

このように複数のチャンネル通知用urlを1つのappから作成することもできる

curlでテストする

curl -X POST \
  -H 'Content-type: application/json' \
  --data '{"text":":tada: Slackへの通知テスト成功! from curl"}' \
  https://hooks.slack.com/services/your/incoming/hook

ここまでがlaravel関係ない事前準備となる


laravelから使う

ライブラリー入れる

composer require laravel/slack-notification-channel

バージョン2とバージョン3で結構違う。ここではlaravel12の環境なのでバージョン3が入る。一度確認しておくこと

composer show laravel/slack-notification-channel
name     : laravel/slack-notification-channel
descrip. : Slack Notification Channel for laravel.
keywords : laravel, notifications, slack
versions : * v3.5.0
...

ここではv3.5.0が入ったのがわかる

notificationで使う

ここではlaravelの「通知」機能を使いコマンドからこの通知を呼び出す事でslackに送信する

コマンド → slack通知レイヤー → 実際にslackに通知

notificationを作成する

これはslack通知レイヤーであるわけだが、SlackNotifyという名前で作成する。

php artisan make:notification SlackNotify

すると app/Notifications/SlackNotify.phpができる

app/Notifications/SlackNotify.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class SlackNotify extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the notification's delivery channels.
     *
     * @return array<int, string>
     */
    public function via(object $notifiable): array
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     */
    public function toMail(object $notifiable): MailMessage
    {
        return (new MailMessage)
            ->line('The introduction to the notification.')
            ->action('Notification Action', url('/'))
            ->line('Thank you for using our application!');
    }

    /**
     * Get the array representation of the notification.
     *
     * @return array<string, mixed>
     */
    public function toArray(object $notifiable): array
    {
        return [
            //
        ];
    }
}

このように基本的にmailベースのテンプレートが出力されるので、これを変更していく、のだが、

まずvia()をみてみよう

    public function via(object $notifiable): array
    {
        // return ['slack'];
        return [SlackWebhookChannel::class];
    }

webを検索するとおそらく大多数のドキュメントがslackをreturnしているはずだが、これを使うとslack bot(Block Kit)の方になっちゃうんだよな。つまり単純にwebhook urlを使いたいだけの場合これじゃ動かんからね。

+use Illuminate\Notifications\Channels\SlackWebhookChannel;
+

 class SlackNotify extends Notification
 {
@@ -14,10 +19,9 @@ class SlackNotify extends Notification
     /**
      * Create a new notification instance.
      */
-    public function __construct()
-    {
-        //
-    }
+    public function __construct(
+        protected string $message
+    ) {}

     /**
      * Get the notification's delivery channels.
@@ -26,7 +30,21 @@ public function __construct()
      */
     public function via(object $notifiable): array
     {
-        return ['mail'];
+        // return ['slack'];
+        return [SlackWebhookChannel::class];
+    }

こんな風にSlackWebhookChannelを指定してほいほい書き換えていく。なおコンストラーで通知メッセージを受けとれるようにしてる(php8のコンストラクタープロモーションで書いとる)

toSlack()を書く

そしたらtoMail()と同じようにtoSlack()を書いていく。

    public function toSlack($notifiable)
    {
        return (new SlackMessage)
            ->content($this->message);
    }

WebHookの場合channelを選びようがないので、基本的にcontent()で送信するだけ、なんだけど、このSlackMessageに関しては

// use Illuminate\Notifications\Slack\SlackMessage;
use Illuminate\Notifications\Messages\SlackMessage;

こっちを呼びこむ。コメントされているSlack/SlackMessageは Block Kit API 対応の新しいクラス。Slack Bot + OAuth トークンで chat.postMessage API を使うため、これを使わないように!

Notification呼び出しコマンドを作る

make:commandで作ったコマンドから送ってみよう。

php artisan make:command SlackTestCommand

するとapp/Console/Commands/SlackTestCommand.phpが出来る。これはslack通知レイヤーを呼びだすためのコマンドと思ってもらえばok

ここでは全文記述する

app/Console/Commands/SlackTestCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Notification;
use App\Notifications\SlackNotify;

class SlackTestCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:slack-test-command';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        Notification::route('slack', 'https://hooks.slack.com/services/your/incoming/hook')
            ->notify(new SlackNotify('🚀 Slackへ通知が飛びました!'));

        logger('Slack通知を送信しました!');
    }
}

ここではslack webhook urlをベタに書いており非常に取り回しが悪いが、テストスクリプトならまあこんなもんでokだろう。

ログをslackに転送

.env

LOG_CHANNEL=stack
LOG_STACK=single,slack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
LOG_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/debug/incoming/hook
LOG_SLACK_USERNAME="Laravel Log"
LOG_SLACK_EMOJI=":rotating_light:"

とかにする。hookのurlは通知のやつと分けてもいいし分けなくてもいいけど、webhookを使うと特定のchannelにしか送信できない。slackbotに関しては何も触れていないが、ここではbotは使う事は多分できないんじゃないかな

こんな感じでexceptionが飛んでくる。

productionで動かしているアプリケーションは言うまでもなく必ずこの手のエラーは拾わないといけない。ログに書きっぱなしは最悪ですからね。

実際の運用に関して

基本的にはシステム通知で使うものだろうと思う。もちろんユーザーごとにslack_hook_urlとかをDBののカラム作って収めてユーザーごとにそれぞれ通知とかやってもいいんだけど、基本的にはアプリからシステムへ通知するためのものだろう。従来はemailを使わざるを得なかったが、emailは今日日結構面倒くさいじゃないですか、SESとか経由するとかなるとさらに面倒くさい。なのでslackで通知しちゃった方がいいんじゃないですかね

上記で言及したような割と深刻なエラーを即座につかまえたいとなるとやはりslackの方が使い勝手がいいような気もします。ただ、量が多いとだるいけど、まあそれはメールにしてもそうなわけで。。。

Discussion