LINE Notifyサービス終了のためMessagingAPIに移行してみた【Laravel】

プログラミング

こんにちは!ケイマエです。

2025年3月末でLINE Notifyがサービス終了になるので、Messaging APIを利用して置き換えしました!

LINE NotifyからMessaging APIへの移行の肝になってくるのが、グループIDを取得する方法です。

Google Spread Sheetの拡張機能のApps Scriptを使ってグループIDを取得する記事は多々ありましたが、私はLaravelのWEBサービスに組み込んで実装しました。

今回はその方法を紹介する記事になります。

興味がある方は見ていってください!

さっそくいきましょう!!

 

LINE Notifyとはどういうサービス?

まずLINE Notifyがどういうサービスか一言で説明します。

「LINEのグループごとにAPIキーを発行することができて、そのキーを使ってAPIを叩くと、そのグループにメッセージを送ることができるサービスです。」

これを利用することで、かなり簡単にLINE通知機能が実装できていました。

しかも無料だったので重宝していた方も多いと思います。

しかし、3月末でサービス終了となります(泣)

これからはLINE Messaging API を使ってくださいということです。

 

LINE Messaging API は有料か?

Messaging APIは送れるメッセージの数に制限がありますが、無料で使うこともできます!

料金プランが気になる方はこちら

200通まで無料となっていますが、グループの人数分消費されるので、5人のグループだったら月40通までとなります。

個人的なものであれば無料で事足りそうですね。

メッセージを送るだけでなく色々なAPIも提供されています。

APIリファレンスが気になる方はこちら

今回は使用するものだけピックアップしていきます。

 

LINE Messaging API を使うには?

Messaging APIを使うには、LINE公式アカウントが必要になります。

そしてMessaging API用のチャネルというものを作成して、アクセストークンなどを発行していきます。

API用のチャネルの作り方まではこちらのQiita記事に詳しく書いてあるので、ここでは省きます。

問題はここからで、Messaging APIでグループにメッセージを送るためにグループIDを取得する必要が出てきます。

グループIDを取得する方法は、グループに公式アカウントが参加するときに発火されるWebhookイベントを受信することで、そのレスポンスからグループIDを取得できます。

いろいろ調べましたが、現状Webhookを介してでしかグループIDを取得できません。(2025年3月時点)

先のQiita記事では、このWebhookを受信する方法として、Googleスプレッドシートの拡張機能を使っていましたが、私はLaravelのサービスに組み込んで作りました。

実際にコードを書いて説明していきたいと思います。

 

実際のLaravelでのコーディング

まずwebhookを受信するルートを書きます。

web.php

Route::post('/line/messaging_api_webhook','API\Line\LineMessagingApiController@handleWebhook');

 

そのルートの先の処理をコントローラーに書きます。

LineMessagingApiController.php

<?php

namespace App\Http\Controllers\API\Line;

use App\Http\Controllers\Controller;
use App\Models\LineGroup;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Http\Request;

class LineMessagingApiController extends Controller
{
  private $client = null;
  public function __construct()
  {
    $client = new Client(['base_uri' => config('line.messaging_api.base_uri')]);
    $this->client = $client;
  }

  /**
  * LINE Messaging APIのWebhook受信API
  * (グループ参加イベントの場合、そのグループの情報を保存する)
  */
  public function handleWebhook(Request $request)
  {
    $signature = $request->header('X-Line-Signature');
    $body = $request->getContent();
    $channel_secret = env('LINE_CHANNEL_SECRET');

    // 署名の検証
    if (!$this->isValidSignature($signature, $body, $channel_secret)) {
      return response()->json(['error' => 'Unauthorized'], 401);
    }

    // Webhookイベントの処理
    $events = json_decode($body, true)['events'] ?? [];
    foreach ($events as $event) {
      if ($event['type'] === 'join') {
        $group_id = $event['source']['groupId'] ?? null;
        if ($group_id) {
          $summary = $this->getGroupSummary($group_id);
          $group = LineGroup::where('group_id', $group_id)->where('name', $summary['groupName'])->first();
          // LINEグループを作成
          if (is_null($group)) {
            LineGroup::create([
              'group_id' => $group_id,
              'name' => $summary['groupName'],
            ]);
          }
        }
      }
    }
    return;
  }

  /**
  * グループトークの概要を取得する
  *
  * @param string $group_id
  */
  public function getGroupSummary($group_id)
  {
     return $this->request('GET', config('line.messaging_api.path.group_summary')($group_id));
  }

  /**
  * LINE Webhookの署名を検証
  */
  private function isValidSignature($signature, $body, $channel_secret)
  {
    $hash = hash_hmac('sha256', $body, $channel_secret, true);
    $expected_signature = base64_encode($hash);
    return hash_equals($expected_signature, $signature);
  }

  /**
  * リクエストを送信する
  *
  * @param string $method HTTPメソッド
  * @param string $endpoint エンドポイント
  * @param array $options リクエストオプション
  */
  private function request($method, $endpoint, array $options = [])
  {
    try {
      $defaultOptions = [
        'headers' => [
          'Authorization' => 'Bearer ' . env('LINE_CHANNEL_ACCESS_TOKEN')
        ],
      ];
      $options = array_replace_recursive($defaultOptions, $options);

      $response = $this->client->request($method, $endpoint, $options);

      return json_decode($response->getBody()->getContents(), true);

    } catch (ClientException $e) {
      \Log::error($e->getResponse()->getBody()->getContents());
    }
  }
}

Webhook受信APIのhandleWebhook関数の内容について補足していきます。

以下の処理は署名の検証をしています。オープンに公開されるURLのため、不特定のアクセスを除外するためです。

    $signature = $request->header('X-Line-Signature');
    $body = $request->getContent();
    $channel_secret = env('LINE_CHANNEL_SECRET');

    // 署名の検証
    if (!$this->isValidSignature($signature, $body, $channel_secret)) {
      return response()->json(['error' => 'Unauthorized'], 401);
    }

API用のチャネルでLINEチャネルシークレットが分かるので、それを.envに保持して、突き合わせて認証を行う形です。

署名の検証処理やisValidSignature関数は公式で提供されているものをそのまま利用しています。

 

次に認証後のWebhookイベント処理(グループID取得方法)を補足していきます。

    // Webhookイベントの処理
    $events = json_decode($body, true)['events'] ?? [];
    foreach ($events as $event) {
      if ($event['type'] === 'join') {
        $group_id = $event['source']['groupId'] ?? null;
        if ($group_id) {
          $summary = $this->getGroupSummary($group_id);
          $group = LineGroup::where('group_id', $group_id)->where('name', $summary['groupName'])->first();
          // LINEグループを作成
          if (is_null($group)) {
            LineGroup::create([
              'group_id' => $group_id,
              'name' => $summary['groupName'],
            ]);
          }
        }
      }
    }

レスポンスのeventsプロパティに各イベントが格納されているので、foreachでひとつずつ処理していきます。

欲しいイベントはグループ参加イベントなので、$event[‘type’] === ‘join’ としています。

グループ参加イベントの場合、$event[‘source’][‘groupId’] でグループIDが取得できます。

一応これで必要なグループIDは取得できるのですが、グループ概要を取得するAPIを叩いてグループ名を取得し、それをline_groupsテーブルにグループIDとグループ名をセットで保存するということをしています。

 

以上で、グループIDの取得ができました!

次にそのグループにメッセージを送信する方法を見ていきます。

LineMessagingApiController.phpに以下を追加

public function sendMessage($accessToken, $to)
{
  $options = [
    'headers' => [
      'Content-Type' => 'application/json',
    ],
    'json' => [
      'to' => $to,
      'messages' => [
        'type' => 'text',
        'text' => 'メッセージ', // 改行は"\n"を入れればできます
      ];
    ],
  ];

  return $this->request('POST', 'https://api.line.me/v2/bot/message/push', $accessToken, $options);
}

$options[‘json’][‘to’] にグループIDをセットして、$options[‘json’][‘messages’] は上のような形にしてリクエストすることで、そのグループにメッセージを送ることができます。

まぁAPIリファレンスを見ればすぐ分かることなのですが。

 

以上で、LINE Notifyを利用したLINEグループへの通知機能をMessaging APIを使って置き換えることができました!

 

あとがき

たまにはプログラミングの記事も書かなかればと思い、久しぶりに書いてみました(笑)

正直ChatGPTを使えばコーディングはできる時代になったので、サンプルコードも不要かなと思いつつ…

今回LINEのAPIリファレンスを色々見てて、けっこうな種類のAPIが提供されているので、色々できそうだなぁと思案が広がったのが良かったです。

よろしければ他の商品紹介の記事なども見ていってください!

それではまたの記事でノシ