
はじめに:LINE Botの可能性を広げる画像処理とAI連携
LINE Botは、単なるテキスト応答ツールにとどまらず、画像やスタンプ、動画などの多様なメディアに対応することで、その対話能力を飛躍的に向上させることが可能です。本記事では、LINE Botが画像メッセージを受信し、それをGoogle Driveに保存する一連のプロセス、さらにはOpenAIのChatGPT API(GPT-4oなど)と連携して画像の内容を解析し、自動応答に活用する方法について、Google Apps Script (GAS) を用いた実装手順を詳細に解説します。
1. LINE Botにおける画像メッセージの検出と処理フロー
LINE Botがユーザーから画像メッセージを受信した際、そのメッセージタイプを正確に識別し、適切な処理に分岐させる必要があります。LINE Messaging APIから送られてくるWebhookイベントのペイロードに含まれるevent.message.typeプロパティが'image'であるかを判定することで、画像メッセージを検出します。これにより、画像メッセージは専用の関数へ引き渡され、画像特有の処理が開始されます。
// doPost 関数:受信メッセージのタイプに応じた処理の分岐
function doPost(e) {
// ... (処理の省略) ...
events.forEach(event => {
if (event.type === 'message') {
if (event.message.type === 'text') {
doPostText(event); // テキストメッセージの処理
} else if (event.message.type === 'sticker') {
doPostSticker(event); // スタンプメッセージの処理
} else if (event.message.type === 'image') { // 画像メッセージの検出
doPostImage(event); // 画像専用の処理関数を呼び出し
}
}
});
}
2. 受信した画像ファイルのGoogle Driveへの永続化
LINEから画像が送られてきたら、それは一時的なデータです。永続的に保存するには、どこかに置いておく必要があります。そこで今回は、一般的によく使うGoogle Driveに保存することにしました。LINEの画像データは、メッセージIDを使ってLINEのAPIから直接ダウンロードします。ダウンロードしたデータはBlob形式で取得できるので、それをそのままDriveに放り込みます。
具体的には、LINEのメッセージIDを用いて画像コンテンツのURLを構築し、GASのUrlFetchApp.fetchメソッドで画像データを取得する。取得したデータはBlob形式となるため、DriveApp.createFileメソッドを用いてGoogle Drive上の指定フォルダに保存される。この際、保存されたファイルのIDが取得され、後続の処理で利用されることになる。この画像保存処理を独立した関数とすることで、コードの可読性と再利用性が向上します。
// doPostImage 関数の一部(画像のダウンロードとDrive保存の起点)
function doPostImage(event) {
const messageId = event.message.id;
const replyToken = event.replyToken;
const userId = event.source.userId;
const lineChannelAccessToken = LINE_CHANNEL_ACCESS_TOKEN;
// 1. LINE Messaging APIからの画像コンテンツダウンロード
const imageUrl = `https://api-data.line.me/v2/bot/message/${messageId}/content`;
const imageResponse = UrlFetchApp.fetch(imageUrl, {
headers: { 'Authorization': 'Bearer ' + lineChannelAccessToken, }
});
const imageBlob = imageResponse.getBlob(); // Blob形式で画像データを取得
// 2. Google Driveへの画像保存処理(専用関数による抽象化)
const saveResult = saveImageToDrive(imageBlob);
let savedFileId = '';
if (saveResult.success && saveResult.fileId) {
savedFileId = saveResult.fileId;
replyToLine(replyToken, "画像をGoogle Driveに保存しました。");
} else {
Logger.log("Drive保存エラー: " + saveResult.error);
replyToLine(replyToken, "申し訳ありません、画像の保存に失敗しました。");
}
// ... (後続の処理:スプレッドシートへのログ記録) ...
}
// saveImageToDrive 関数:Google Driveへの画像ファイル保存ロジック
/**
* Blob形式の画像をGoogle Driveに保存します。
* @param {GoogleAppsScript.Base.Blob} imageBlob 保存する画像のBlobデータ
* @returns {{success: boolean, fileId?: string, error?: string}} 保存結果。成功時はfileIdを含む
*/
function saveImageToDrive(imageBlob) {
try {
const folderId = GDRIVE_IMAGE_FOLDER_ID; // スクリプトプロパティで指定された保存先フォルダID
let folder = folderId ? DriveApp.getFolderById(folderId) : DriveApp.getRootFolder();
const timestamp = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyyMMdd_HHmmss');
const fileName = `LINE_Image_${timestamp}.jpg`; // ファイル名の生成
const file = folder.createFile(imageBlob.setName(fileName));
return { success: true, fileId: file.getId() }; // 保存されたファイルのIDを返却
} catch (e) {
return { success: false, error: e.toString() };
}
}
3. 受信画像情報の「ChatLog」シートへの記録
Botの運用において、ユーザーとの対話履歴を詳細に記録することは不可欠です。Google Spreadsheetをデータベースとして活用し、受信した画像メッセージに関する以下の情報をChatLogシートに追記します。
- A列: メッセージ受信日時
- B列: 送信ユーザーID
- C列: 送信ユーザー名(LINEプロフィールから取得)
- G列: Google Driveに保存された画像のファイルID
// doPostImage 関数(抜粋):ChatLogシートへの情報追記
function doPostImage(event) {
// ... (画像ダウンロード、Drive保存の処理、savedFileIdの取得) ...
// 3. ChatLogシートへの記録
try {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('ChatLog');
if (!sheet) throw new Error(`指定されたシート「ChatLog」が見つかりません。`);
const now = new Date(); // 現在時刻
const profile = getLineProfile(userId); // ユーザー名取得関数(別途定義)
const userName = profile ? profile.displayName : 'Unknown';
// スプレッドシートへ追記する行データ
const newRow = [
now, // A列:現在時刻
userId, // B列:ユーザーID
userName, // C列:ユーザー名
'', // D列 (空)
'', // E列 (空)
'', // F列 (空)
savedFileId // G列:保存されたGoogle DriveファイルID
];
sheet.appendRow(newRow); // 行をシートに追加
Logger.log('ChatLogシートに画像情報を追記しました。');
} catch (e) {
Logger.log("ChatLogシートへの追記エラー: " + e.toString());
}
}
これにより、いつ、誰から、どの画像が送られてきたかの履歴が明確に管理され、後の分析やデバッグに役立ちます。ユーザー名の取得には、LINEのユーザープロフィール取得APIを利用し、ユーザーIDから表示名を取得する。
4. OpenAI ChatGPT API(GPT-4o)による画像解析とAI連携
LINE Botに画像認識能力を付与する最大の目的は、AIによる画像解析とそれに基づいた応答の実現です。OpenAIが提供するchatGPT API(gpt-4oなどのモデル)を利用することで、画像の内容を理解し、テキストとしてその特徴や情報を抽出することが可能になります。
chatGPT APIへ画像を送信する際には、画像データを直接ファイルとして渡すのではなく、Base64エンコードされた文字列としてJSONペイロードに含める必要があります。Google Driveに保存された画像は、DriveApp.getFileByIdでファイルオブジェクトを取得し、getBlob().getBytes()でバイト配列に変換後、Utilities.base64EncodeでBase64文字列にエンコードされる。
また、LINE Botへのテキストメッセージ時には、過去にユーザーが送信した未返信の画像もAIに提示することで、より文脈を考慮した応答を生成させることができる。未返信の画像ファイルIDはスプレッドシートから取得され、それぞれBase64エンコードされてChatGPT APIのリクエストに含まれる。
// getDriveImageAsBase64 関数:Google Drive上の画像をBase64形式に変換
/**
* Google Driveに保存された画像ファイルを読み込み、Base64エンコードします。
* @param {string} fileId Google DriveのファイルID
* @returns {{success: boolean, base64Image?: string, mimeType?: string, error?: string}}
* 成功時はBase64文字列とMIMEタイプを、失敗時はエラー情報を返します。
*/
function getDriveImageAsBase64(fileId) {
try {
const file = DriveApp.getFileById(fileId);
const blob = file.getBlob();
const base64Image = Utilities.base64Encode(blob.getBytes()); // Base64エンコード実行
const mimeType = blob.getContentType();
return { success: true, base64Image: base64Image, mimeType: mimeType };
} catch (e) {
Logger.log("Drive画像読み込み&Base64エンコードエラー: " + e.toString());
return { success: false, error: e.toString() };
}
}
// getChatGPTResponse 関数(抜粋):ChatGPT APIへのリクエスト構築
function getChatGPTResponse(userId, userMessage, imageFileIds) {
let messages = [
{ role: "system", content: systemMessageContent } // システムプロンプト
];
// ... (過去のテキスト会話履歴の追加) ...
// 未返信の画像ファイルIDを順次取得し、Base64エンコードしてAIリクエストに追加
for (let i = imageFileIds.length - 1; i >= 0; i--) {
const driveImage = getDriveImageAsBase64(imageFileIds[i]);
if (driveImage.success) {
const base64Image = driveImage.base64Image;
const mimeType = driveImage.mimeType;
messages.push({
role: "user",
content: [
{ type: "image_url", image_url: { url: `data:${mimeType};base64,${base64Image}` } }
]
});
} else {
Logger.log(`画像ID ${imageFileIds[i]} のエンコードに失敗: ${driveImage.error}`);
}
}
// OpenAI APIへリクエストを送信
const url = "https://api.openai.com/v1/chat/completions";
const options = {
"method": "post",
"headers": {
"Authorization": "Bearer " + OPENAI_API_KEY,
"Content-Type": "application/json"
},
"payload": JSON.stringify({
"model": "gpt-4o", // 対応モデルを指定
"messages": messages,
"max_tokens": MAX_TOKEN
})
};
// ... (API応答の処理) ...
}
この連携により、Botは単にテキストで質問に答えるだけでなく、送られてきた画像や過去にやり取りされた画像を"見て"、それに基づいた高度な対話が可能となります。
5. 【重要】環境設定とデプロイに関する留意事項
本システムを運用するにあたり、以下の設定と手順が不可欠です。
- スクリプトプロパティの利用:
LINE_CHANNEL_ACCESS_TOKEN、OPENAI_API_KEY、GDRIVE_IMAGE_FOLDER_IDなどの機密情報や頻繁に変わる設定値は、Google Apps Scriptのスクリプトプロパティに登録し、コード内から安全に取得するようにしてください。これにより、コードのセキュリティが向上し、管理が容易になります。 - Google Apps Scriptの権限承認:
DriveApp、SpreadsheetApp、UrlFetchAppなどの各サービスを利用するためには、スクリプト実行時にGoogleアカウントの承認が必要です。初回実行時または権限変更時に、求められる承認を許可してください。 - デプロイの実行: コードの変更をLINE Botのウェブアプリに反映させるためには、Google Apps Scriptエディタで新しいデプロイを行うか、既存のデプロイを更新する必要があります。これを怠ると、修正が適用されず、古いバージョンのコードが実行され続けます。
まとめ
本記事では、LINE Botが画像メッセージを効率的に処理し、Google Driveへの保存、さらにはOpenAI ChatGPT APIとの連携により画像認識能力を付与する一連のシステム構築について解説しました。これらの機能は、Botの対話体験を豊かにし、より高度なユーザーサポートや情報提供を可能にします。
Google Apps Scriptの柔軟性を活用することで、サーバーレスでこれらの機能を実装できるため、LINE Bot開発の敷居を大きく下げることができます。ぜひ本ガイドを参考に、ご自身のLINE Botに新たな価値を加えてみてください。
