[ #LINEDC ] WebhookイベントからChat IDを取得する

この記事では、LINE Botの開発において重要なChat IDの取得方法について解説しています。WebhookイベントからChat IDを効率的に抽出する汎用的な関数の実装例と、Chat IDを活用したユースケースの具体例(非同期処理のローディング通知など)が紹介されています。

広告ここから
広告ここまで

目次

    LINE MessagingのBotを開発していると、ユーザーからのメッセージやアクションに対して応答するために、まずはそのメッセージがどこから来たのかを特定する必要があります。つまり、「誰が」または「どのグループが」そのアクションを起こしたのかを把握することが重要です。今回はLINE Webhookイベントから効率的にChat IDを抽出する方法について解説します。

    なぜChat IDの取得が重要なのか?

    LINE Botの開発において、Chat IDは非常に重要な要素です。このIDを使って以下のようなことが実現できます:

    • 特定のユーザーやグループにメッセージを送信する
    • ユーザーごとのコンテキストや状態を管理する
    • グループ内での特定の処理を実行する
    • メッセージの送信元を識別してパーソナライズされた体験を提供する

    特に複雑な会話フローを設計する場合や、処理に時間がかかる非同期操作(例:AIによる画像生成など)を行う場合には、後から「誰に対して」返信すべきかを判断するためにChat IDが必須となります。

    LINE Messaging APIとWebhookの基本

    実装に入る前に、LINE Messaging APIとWebhookの基本を簡単に押さえておきましょう。

    LINE Messaging APIのWebhookとは

    LINE Messaging APIでは、ユーザーがBotに対して行ったアクション(メッセージ送信、ボタンタップなど)が発生すると、そのイベント情報がWebhookとして指定されたエンドポイントにPOSTリクエストとして送信されます。このWebhookには様々な情報が含まれていますが、その中にはイベントの送信元情報(source)も含まれています。

    送信元の種類

    LINE Messaging APIでは、イベントの送信元は大きく3つのタイプに分類されます:

    1. user: 個人のユーザーからのイベント(1対1のトーク)
    2. group: グループチャットからのイベント
    3. room: トークルームからのイベント(複数人トーク)

    それぞれの送信元タイプに対応するIDフィールドが異なるため、適切に抽出する必要があります。

    Chat ID取得関数の実装と解説

    では、実際にWebhookイベントからChat IDを抽出する関数を見ていきましょう。以下は TypeScript で実装した汎用的な関数です:

    import { WebhookEvent } from '@line/bot-sdk';
    
    // 関数の戻り値の型定義
    interface ChatIdResult {
      type: 'user' | 'group' | 'room' | null;
      id: string | null;
      found: boolean;
    }
    
    /**
     * LINE WebhookイベントからチャットのタイプとそのIDを抽出する関数
     * @param event - LINE Webhookから受け取ったイベントオブジェクト
     * @returns 抽出した情報を含むオブジェクト
     */
    function extractChatId(event: WebhookEvent | undefined): ChatIdResult {
      // デフォルト値
      const result: ChatIdResult = {
        type: null,
        id: null,
        found: false
      };
    
      // イベントの存在確認
      if (!event || !event.source) {
        return result;
      }
    
      // ソースタイプに応じてIDを抽出
      switch (event.source.type) {
        case 'user':
          result.type = 'user';
          result.id = event.source.userId || null;
          result.found = !!result.id;
          break;
        case 'group':
          result.type = 'group';
          result.id = event.source.groupId || null;
          result.found = !!result.id;
          break;
        case 'room':
          result.type = 'room';
          result.id = event.source.roomId || null;
          result.found = !!result.id;
          break;
      }
    
      return result;
    }

    コードの解説

    この関数は以下のような処理を行っています:

    1. 戻り値の型定義:
      ChatIdResult インターフェースを定義し、結果としてチャットのタイプ、ID、および取得成功の有無を返します。
    2. デフォルト値の設定:
      初期状態では何も見つからなかった状態(null値とfound: false)を設定しています。
    3. イベントの妥当性確認:
      イベントオブジェクトやsourceプロパティが存在しない場合は早期リターンします。これにより、不正なイベントに対する防御的プログラミングを実現しています。
    4. 送信元タイプの判定とID抽出:
      switch文を使って送信元のタイプに応じた処理を行い、適切なIDフィールドから値を抽出します。

    • userの場合: userIdを取得
    • groupの場合: groupIdを取得
    • roomの場合: roomIdを取得

    1. ID取得の成功判定:
      !!result.idによって、IDが正常に取得できたかどうかをboolean値に変換してfoundフィールドに格納しています。

    実際の使用例

    この関数を実際のWebhook処理でどのように使用するかを見てみましょう:

    // 使用例
    function handleWebhook(req: {body: {events?: LineEvent[]}}, res: {status: (code: number) => {end: () => void}}) {
      const events = req.body.events || [];
    
      events.forEach((event: LineEvent) => {
        const chatInfo = extractChatId(event);
    
        if (chatInfo.found) {
          console.log(`チャットタイプ: ${chatInfo.type}, ID: ${chatInfo.id}`);
          // このIDを使用して何かの処理を行う
          // 例えばローディングを出す
          startChatLoading(chatInfo.id, 10)
        } else {
          console.log('チャットIDが見つかりませんでした');
        }
      });
    
      // 200 OKを返す
      res.status(200).end();
    }

    このコードでは:

    1. Webhookリクエストから取得したイベント配列をループ処理します
    2. 各イベントに対してextractChatId関数を呼び出してチャット情報を取得します
    3. チャット情報が見つかった場合、そのIDを使用して何らかの処理(例:startChatLoading)を実行します
    4. 最後にステータスコード200で応答を返して正常に受信したことをLINEプラットフォームに通知します

    応用例:Chat IDを活用したユースケース

    Chat IDを取得できたら、様々な活用方法があります。ここでは具体的なユースケースをいくつか紹介します。

    1. 非同期処理のローディング状態通知

    処理に時間がかかる操作(例:画像生成やAI処理)を行う場合、ユーザーに「処理中…」などのメッセージを送信し、完了したら結果を返すというフローを実装できます:

    async function handleImageGenerationRequest(event: WebhookEvent) {
      const chatInfo = extractChatId(event);
    
      if (!chatInfo.found) return;
    
      // 処理中メッセージを送信
      await lineClient.replyMessage(chatInfo.id, {
        type: 'text',
        text: '画像を生成中です。少々お待ちください...'
      });
    
      try {
        // 時間のかかる処理を実行
        const generatedImageUrl = await generateAIImage(event.message.text);
    
        // 結果を送信
        await lineClient.pushMessage(chatInfo.id, {
          type: 'image',
          originalContentUrl: generatedImageUrl,
          previewImageUrl: generatedImageUrl
        });
      } catch (error) {
        // エラー時の通知
        await lineClient.pushMessage(chatInfo.id, {
          type: 'text',
          text: '画像生成に失敗しました。後ほど再度お試しください。'
        });
      }
    }

    2. ユーザー/グループ別の状態管理

    Chat IDを使ってDB上でユーザーやグループごとの状態を管理することができます:

    async function handleConversation(event: WebhookEvent) {
      const chatInfo = extractChatId(event);
    
      if (!chatInfo.found) return;
    
      // DBからこのチャットの会話状態を取得
      const conversationState = await database.getConversationState(chatInfo.type, chatInfo.id);
    
      // 状態に応じた処理
      switch (conversationState.currentStep) {
        case 'WAITING_NAME':
          // 名前を保存
          await database.updateUserInfo(chatInfo.id, { name: event.message.text });
          await lineClient.replyMessage(event.replyToken, {
            type: 'text',
            text: 'ありがとうございます!次に年齢を教えてください。'
          });
          // 状態を更新
          await database.updateConversationState(chatInfo.type, chatInfo.id, { currentStep: 'WAITING_AGE' });
          break;
    
        case 'WAITING_AGE':
          // 年齢を保存して完了
          // ...省略...
          break;
    
        default:
          // 初回メッセージなど
          // ...省略...
      }
    }

    3. グループとユーザーの区別による機能分岐

    Chat IDのタイプによって機能を分岐させることも可能です:

    function handleCommand(event: WebhookEvent) {
      const chatInfo = extractChatId(event);
    
      if (!chatInfo.found) return;
    
      // コマンドテキストを取得(例: !help)
      const command = event.message.text;
    
      // チャットタイプに応じた処理
      if (chatInfo.type === 'user') {
        // 個人チャット専用コマンド処理
        handlePersonalCommand(command, chatInfo.id, event.replyToken);
      } else if (chatInfo.type === 'group') {
        // グループ専用コマンド処理
        handleGroupCommand(command, chatInfo.id, event.replyToken);
      } else if (chatInfo.type === 'room') {
        // ルーム専用コマンド処理
        handleRoomCommand(command, chatInfo.id, event.replyToken);
      }
    }

    まとめ

    LINE MessagingのWebhookイベントからChat IDを適切に抽出することは、Bot開発において基本的かつ重要なスキルです。今回紹介したextractChatId関数を使えば、イベントのソースタイプに関わらず統一的にChat IDを取得できます。

    この関数の利点として以下が挙げられます:

    • 型安全: TypeScriptによる型定義で安全性が高い
    • 汎用性: ユーザー、グループ、ルームのすべてのタイプに対応
    • 堅牢性: nullチェックなど防御的プログラミングが施されている
    • 使いやすさ: 統一されたインターフェースで結果を返す

    Chat IDをうまく活用することで、より高度なBotの実装が可能になります。例えば非同期処理のステータス通知、ユーザー/グループごとの状態管理、チャットタイプに応じた機能分岐など、様々なユースケースに対応できるようになります。

    みなさんもぜひこの関数を使って、より機能的なLINE Botを開発してみてください!

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark