[ #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つのタイプに分類されます:
- user: 個人のユーザーからのイベント(1対1のトーク)
- group: グループチャットからのイベント
- 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;
}
コードの解説
この関数は以下のような処理を行っています:
- 戻り値の型定義:
ChatIdResult
インターフェースを定義し、結果としてチャットのタイプ、ID、および取得成功の有無を返します。 - デフォルト値の設定:
初期状態では何も見つからなかった状態(null値とfound: false)を設定しています。 - イベントの妥当性確認:
イベントオブジェクトやsourceプロパティが存在しない場合は早期リターンします。これにより、不正なイベントに対する防御的プログラミングを実現しています。 - 送信元タイプの判定とID抽出:
switch
文を使って送信元のタイプに応じた処理を行い、適切なIDフィールドから値を抽出します。
- userの場合:
userId
を取得 - groupの場合:
groupId
を取得 - roomの場合:
roomId
を取得
- 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();
}
このコードでは:
- Webhookリクエストから取得したイベント配列をループ処理します
- 各イベントに対して
extractChatId
関数を呼び出してチャット情報を取得します - チャット情報が見つかった場合、そのIDを使用して何らかの処理(例:
startChatLoading
)を実行します - 最後にステータスコード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を開発してみてください!