tech. tt

Teens Town 技術ブログ

Laravelで動的に定期実行スクリプトを実装した話

いきさつ

弊団体ポータルシステムでは、Slackのカスタムメッセージを定期送信することができます。
中身はLaravelのスケジューラーを使っているのですが、これをちょいと工夫してダイナミックに送れるようにした話。

環境

  • Laravel Ver. 6.x
  • CentOS Ver. 7.x
  • MySQL Ver 5.7

1. Modelの作成

php artisan make:model -m SlackBotMessageで、Slackメッセージ格納用のModelを生成します。

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateSlackBotMessagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('slack_bot_messages', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title'); // 識別用の名前
            $table->string('cron_time'); // cron時間
            $table->longText('message'); // 送信内容
            $table->json('mention_ids'); // メンション先ID配列
            $table->string('channel_id'); // 投稿先channel id
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('slack_bot_messages');
    }
}

2. cron 時間

...って勝手に読んでいますが、unixでcronを設定するときに使う 0 9 * * 5 /path/to/hoge.sh みたいなやつの0 9 * * 5の部分のこと。
Laravel にはこれに基づいて実行してくれる仕組みがあるのでこれを使っています。

割愛しますが、別途CRUDは構築してあげます。

3. コマンド組み込み

そして app/Console/Kernel.php に下記みたいな感じです。

<?php

namespace App\Console;

use App\SlackBotMessage;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        //
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        // 別のコマンドも適宜使う。
        $schedule->command('hogehoge')->everyMinute();

        // このへん。
        $bots = SlackBotMessage::all();
        foreach ($bots as $bot) {
            $schedule->call(function () use ($bot) {
                $ids = "";
                foreach ($bot->mention_ids as $id) {
                    // メンションはSlackのAPIリファレンスに沿って実装。
                    $ids .= "<@" . $id . ">";
                }
                $client = new \GuzzleHttp\Client();
                $res = $client->request('POST', 'https://slack.com/api/chat.postMessage?token='.config('services.slack.bot_oauth_token'), [
                    'form_params' => [
                        'channel' => $bot->channel_id,
                        'text' => $ids . " \n" . $bot->message,
                    ]
                ]);
            })->cron($bot->cron_time);
        }
    }

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}

振り返れば結構簡単かもですね。

余談ですが僕はこんな感じ↓で、曜日を返すヘルパメソッドを作りました。

app/SlackBotMessage.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class SlackBotMessage extends Model
{
    protected $appends = [
        'time_str'
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'mention_ids' => 'array',
    ];

    public function getTimeStrAttribute()
    {
        $setting = \explode(' ', $this->cron_time);
        $minute = $setting[0] == "*" ? "毎分" : $setting[0]."分";
        $hour = $setting[1] == "*" ? "毎時" : $setting[1]."時";
        $day = $setting[2] == "*" ? "毎日" : $setting[2]."日";
        $month = $setting[3] == "*" ? "毎月" : $setting[3]."月";

        $dayOfWeek = null;
        if ($setting[3] != "*") {
            $days = ["日","月","火","水","木","金","土","日"];
            $_dayOfWeek = [];
            for ($i=0; $i < 8; $i++) {
                if (strpos($setting[4], ((string)$i)) !== false) {
                    $_dayOfWeek[] = $days[$i];
                }
            }
            $dayOfWeek = \implode(",", $_dayOfWeek) . " 曜日";
        }

        return ("" . $month . " " . $day . " " . $hour . " " . $minute . ($dayOfWeek ? " (" . $dayOfWeek . ")" : ""));
    }
}