6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gemini×LINE お手軽に生成AIchatbot

Posted at

{0C7743AE-45C7-49CC-B6D9-961602973485}.png

テキストを送ればGeminiのAPIを通してプロンプトに沿った返答をする 返答内容をスプレッドシートに保存
画像を送れば画像のURLをスプレッドシートに保存
という機能が出来ます

1. Googleアカウントを準備する

Googleアカウントを持っていない場合は、Googleアカウントを作成してください。
Googleアカウントが必要です。このアカウントでGoogleドライブやGoogleスプレッドシートにアクセスします。

2. スプレッドシートの準備

Googleドライブで新しいGoogleスプレッドシートを作成します。
名前は何でもいいです。
スプレッドシートに「Sheet1」と「Sheet2」という名前のシートを作成します(デフォルトで存在する場合もあります)。
「Sheet1」は解析結果や画像のURLを保存します。
「Sheet2」はログメッセージを保存します。

3. Google Apps Script (GAS) プロジェクトの作成

スプレッドシートにアクセスし、「拡張機能」→「Apps Script」を選択します。
新しいスクリプトプロジェクトが作成されます。このプロジェクトにコードを書いていきます。

{CAFFC4F7-41A7-42B9-A494-91E05999FEB6}.png

{A0001576-9ED0-4381-98B7-15048E833DE8}.png

3. コードのコピー&ペースト

下記のコード(doPost(e)から始まる)をすべてコピーします。
作成したGoogle Apps Scriptプロジェクトのエディタに貼り付けます。

LINE.gs

function doPost(e) {
  var replyToken;
  try {
    // LINEからのPOSTデータをJSON形式にパース
    var json = JSON.parse(e.postData.contents);
    logEvent("Received event: " + JSON.stringify(json));


    // 最初のイベント情報を取得
    var event = json.events[0];
    replyToken = event.replyToken;

    // メッセージの種類に応じて処理を分岐
    if (event.message.type === "image") {
      handleImageMessage(event, replyToken);
    } else if (event.message.type === "text") {
      handleTextMessage(event, replyToken);
    } else {
      replyToLine(replyToken, "画像またはテキストを送信してください。");
    }
  } catch (error) {
    // エラーが発生した場合、ログに記録し、LINEにエラーメッセージを返信
    logEvent("Error in doPost: " + error.message);
    if (replyToken) {
      replyToLine(replyToken, "エラーが発生しました: " + error.message);
    }
  }
}

function handleImageMessage(event, replyToken) {
  try {
    // 画像のIDを取得
    var imageId = event.message.id;
    // 画像URLを取得するための関数を呼び出し
    var imageUrl = getImageUrl(imageId);

    // 画像を受け取った旨をLINEに返信
    replyToLine(replyToken, "画像を受け取りました。スプレッドシートに保存します。");

    // 画像URLをスプレッドシートに保存
    saveToSheet("画像が送信されました", imageUrl);

    // 画像の保存が完了した旨をLINEに返信
    replyToLine(replyToken, "画像を保存しました。URL: " + imageUrl);
  } catch (error) {
    // エラーが発生した場合、ログに記録し、LINEにエラーメッセージを返信
    logEvent("Error in handleImageMessage: " + error.message);
    replyToLine(replyToken, "画像の処理中にエラーが発生しました: " + error.message);
  }
}

function handleTextMessage(event, replyToken) {
  try {
    // テキストメッセージを取得
    var message = event.message.text;
    var replyMessage = "";

    // テキストをGemini APIで解析
    replyMessage = analyzeTextWithGemini(message);

    // 解析結果をLINEに返信
    replyToLine(replyToken, replyMessage);
  } catch (error) {
    // エラーが発生した場合、ログに記録し、LINEにエラーメッセージを返信
    logEvent("Error in handleTextMessage: " + error.message + " | Stack: " + error.stack);
    replyToLine(replyToken, "テキストの処理中にエラーが発生しました: " + error.message);
  }
}

function getImageUrl(imageId) {
  try {
    // 画像取得のためのログ
    logEvent("Attempting to fetch image with ID: " + imageId);
    
    // 画像データを取得するためのURLを生成
    var url = "https://api-data.line.me/v2/bot/message/" + imageId + "/content";
    // LINE APIを使用して画像データを取得
    var response = UrlFetchApp.fetch(url, {
      headers: {
        "Authorization": "Bearer " + getChannelAccessToken(),
      },
      muteHttpExceptions: true,  // HTTPエラーを無視せずレスポンスを取得
    });

    var responseCode = response.getResponseCode();
    var responseText = response.getContentText();

    // 画像取得のレスポンスをログに記録
    logEvent("LINE API response code for image fetch: " + responseCode);
    logEvent("LINE API response text for image fetch: " + responseText);

    // 画像取得に失敗した場合、エラーをスロー
    if (responseCode !== 200) {
      throw new Error("Failed to fetch image. Response code: " + responseCode + ", Response text: " + responseText);
    }

    // 取得した画像データをGoogleドライブに保存し、URLを取得
    var blob = response.getBlob();
    var file = DriveApp.createFile(blob).setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
    var imageUrl = file.getUrl();

    // 画像URLをログに記録
    logEvent("Image URL successfully retrieved: " + imageUrl);
    return imageUrl;
  } catch (error) {
    // エラーが発生した場合、ログに記録し、エラーメッセージをスロー
    logEvent("Error in getImageUrl: " + error.message + " | Stack: " + error.stack);
    throw new Error("画像URLの取得中にエラーが発生しました。");
  }
}

function analyzeTextWithGemini(message) {
  logEvent("Starting text analysis with Gemini API...");
  try {
    // Gemini APIのAPIキーとURLを設定
    const apiKey = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY'); 
    const url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=' + apiKey;

    const objHeaders = {
      'Content-Type': 'application/json'
    };

    // リクエストボディを設定
    const objPayload = {
      contents: [
        {
          parts: [
            {
              text:  "ユーザーからのLINEメッセージの内容を博識な文豪が書く日記風に変えてください: " + message
            }
          ]
        }
      ]
    };

    // リクエストオプションを設定し、Gemini APIにリクエストを送信
    const objOptions = {
      method: 'post',
      headers: objHeaders,
      payload: JSON.stringify(objPayload),
      //muteHttpExceptions: true
    };

    logEvent("Sending request to Gemini API...");
    const response = UrlFetchApp.fetch(url, objOptions);
    const responseCode = response.getResponseCode();
    const responseText = response.getContentText();

    // Gemini APIのレスポンスをログに記録
    logEvent("Gemini API response code: " + responseCode);
    logEvent("Gemini API response text: " + responseText);

    // APIリクエストが失敗した場合、エラーをスロー
    if (responseCode !== 200) {
      throw new Error("Gemini API request failed with response code: " + responseCode + ", response text: " + responseText);
    }

    // 解析結果をパースして取得
    const jsonResponse = JSON.parse(responseText);
    logEvent("Received response from Gemini API: " + JSON.stringify(jsonResponse));

    // テキストの取得部分を修正し、スプレッドシートに保存
    const description = jsonResponse.candidates[0].content.parts[0].text;
    saveToSheet(description, ""); // テキスト解析結果をシートに保存

    return description;
  } catch (error) {
    // エラーが発生した場合、ログに記録し、エラーメッセージをスロー
    logEvent("Error in analyzeTextWithGemini: " + error.message + " | Stack: " + error.stack);
    throw new Error("Gemini APIの解析中にエラーが発生しました。");
  }
}

function saveToSheet(description, imageUrl) {
  try {
    // シートを取得し、データを保存
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
    sheet.appendRow([description, imageUrl]);  // 解析結果と画像URLをシートに保存
    logEvent("データをスプレッドシートに保存しました。");
  } catch (error) {
    // エラーが発生した場合、ログに記録し、エラーメッセージをスロー
    logEvent("Error in saveToSheet: " + error.message + " | Stack: " + error.stack);
    throw new Error("スプレッドシートへの保存中にエラーが発生しました。");
  }
}

function replyToLine(replyToken, message) {
  try {
    // LINE APIに返信するためのURLとヘッダーを設定
    var url = "https://api.line.me/v2/bot/message/reply";
    var headers = {
      "Content-Type": "application/json",
      "Authorization": "Bearer " + getChannelAccessToken(),
    };
    
    // メッセージが空でないことを確認
    if (!message) {
      throw new Error("Reply message is empty. Cannot send an empty message to LINE.");
    }

    // 返信メッセージを設定し、LINE APIにリクエストを送信
    var postData = {
      "replyToken": replyToken,
      "messages": [{
        "type": "text",
        "text": message
      }]
    };

    var response = UrlFetchApp.fetch(url, {
      "method": "post",
      "headers": headers,
      "payload": JSON.stringify(postData)
    });

    var responseCode = response.getResponseCode();
    var responseText = response.getContentText();

    // 返信結果をログに記録
    logEvent("LINE API response code: " + responseCode);
    logEvent("LINE API response text: " + responseText);

    // 返信が失敗した場合、エラーをスロー
    if (responseCode !== 200) {
      throw new Error("Failed to send message to LINE. Response: " + responseText);
    }

    logEvent("Replied to LINE: " + message);
  } catch (error) {
    // エラーが発生した場合、ログに記録し、エラーメッセージをスロー
    logEvent("Error in replyToLine: " + error.message + " | Stack: " + error.stack);
    throw new Error("LINEへの返信中にエラーが発生しました。");
  }
}

function logEvent(message) {
  try {
    // ログを記録するためのシートを取得または作成
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
    if (!sheet) {
      sheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("Sheet2");
    }
    var date = new Date();
    var formattedDate = Utilities.formatDate(date, Session.getScriptTimeZone(), "yyyy-MM-dd HH:mm:ss");
    sheet.appendRow([formattedDate, message]);  // ログメッセージをSheet2に保存
  } catch (error) {
    // ログ記録のエラーをLoggerに記録
    Logger.log("Error in logEvent: " + error.message + " | Stack: " + error.stack);
  }
}

function getChannelAccessToken() {
  // スクリプトプロパティからアクセス・トークンを取得
  var scriptProperties = PropertiesService.getScriptProperties();
  var token = scriptProperties.getProperty('YOUR_CHANNEL_ACCESS_TOKEN');
  logEvent("Access token retrieved: " + (token ? "Success" : "Failed"));
  return token;
}

{4FA9AFA3-A6C9-4484-904F-CB845975A2AA}.png
こんな感じにはりつけ

↓ ここのtext: のとこにプロンプトを書きます

{455A69EC-89FA-4661-8918-D03568A9ED60}.png

4. 必要なライブラリやトークンの設定

LINEチャネルのアクセストークン取得:

LINE Developers にアクセスし、LINEアカウントでログインします。
新しいチャネルを作成し、アクセストークンを取得します。

参考サイト

Gemini APIキーの取得:

Gemini APIを利用するために、Google Cloud Consoleにアクセスし、APIキーを取得します。

1.Google AI StudioのGemini APIのページにアクセス

Google AI StudioのGemini APIのページ

2.左メニューにある「Get API Key」を選択

3.API Keysのページで「Create API keys in new project」もしくは「APIを作成」をクリックします

{7898610E-963E-4040-A935-7FAB9CA8CAD4}.png
4.少し待つとAPIキーが発行されるので、「Copy」をクリック、APIキーがクリップボードにコピーされます

スクリプトのプロパティ設定

取得したトークンをGoogle Apps Scriptのプロジェクトに保存します。
「プロジェクト」→「プロジェクトのプロパティ」→「スクリプトのプロパティ」からトークンを設定します。
例えば、YOUR_CHANNEL_ACCESS_TOKEN という名前でアクセストークンを設定します。
取得したAPIキーをGoogle Apps Scriptのスクリプトプロパティに設定します。名前はGEMINI_API_KEYとします。
{F9FB2528-9043-4B5A-B81F-AD8014BEEC37}.png
{2FAF135A-EF51-49AD-9334-B59AAAF3E9B8}.png

6. Webアプリケーションとしてデプロイ

Google Apps Scriptエディタで「デプロイ」→「ウェブアプリケーションとしてデプロイ」を選択します。
次のユーザーとして実行を「自分」に設定
アクセスできるユーザーを「全員」に設定
デプロイ後に表示されるURL(ウエブアプリURL)が、LINEのWebhook URLとして使用されます。
{7547BB3F-C536-465B-97B2-B75FB51D044E}.png

7. LINE Developers コンソールでの設定

LINE Developers コンソールに戻り、作成したチャネルの設定ページに移動します。
Webhook URLとして、上記で取得したGoogle Apps ScriptのウェブアプリURLを設定します。
Webhookの使用を有効にします。

※Webhookの利用のチェック忘れずに!!

{AB0D25D0-3171-4164-B340-E6C757865A87}.png

8. テスト

LINEアプリからチャネルにテキストメッセージや画像を送信してみてください。
送信したテキストや画像が正常に解析・保存され、LINEに返信が返ってくることを確認します。
Googleスプレッドシートに解析結果や画像のURLが保存されていることを確認します。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?