Open6

LINE Bot with GAS + Gemini AI

KiKiKi-KiKiKiKiKi-KiKi

LINE Developer アカウントの作成

https://developers.line.biz/console/
📝 公式アカウント名を dev-bot として作成した

Messaging API の利用

2024年9月4日をもって、LINE DevelopersコンソールからMessaging APIチャネルを直接作成することはできなくなりました。
今後は、あらかじめLINE公式アカウントを作成した上で、LINE Official Account Manager (opens new window)でMessaging APIの利用を有効にすることでMessaging APIチャネルを作成できます。

https://developers.line.biz/ja/news/2024/09/04/no-longer-possible-to-create-messaging-api-channels-from-console/

仕様変更後のMessaging APIチャネルの作成方法

仕様変更後は、次の手順でMessaging APIチャネルを作成してください。

  1. LINE公式アカウントを作成する。
  2. 作成したLINE公式アカウントでMessaging APIの利用を有効にする。

Messaging API を有効にする

  1. メニュー > チャット > Messaging API
  2. Messaging API を利用する
  3. プロバイダー名を入力して作成
    📝 dev-test で作成した
KiKiKi-KiKiKiKiKi-KiKi

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

  1. https://developers.line.biz/console/ にログイン
    🚨 公式LINEアカウントのページとは異なるので注意 ⚠️
  2. プロバイダーメニュー内に先ほど作成したプロバイダー名 (dev-test) があるので選択
  3. チャンネル名タブ: 公式アカウント名 dev-bot に MessagingAPI のマークが付いてるのでクリック
  4. Messaging API設定 タブをクリック
  5. 下部に チャネルアクセストークン があるのでトークンを保存しておく
  6. 応答メッセージ は bot から返すので 無効 にしておく
KiKiKi-KiKiKiKiKi-KiKi

GAS の準備 と Webhook の設定

  1. SpredSheet を作成
  2. 拡張機能 > Apps Script を選択

Webhook URL の作成

Apps Script をデプロイすることで LINE BOT と接続するための Webhook URL を作成する

  1. デプロイ > 新しいデプロイ を選択
  2. アクセスできるユーザーを 全員 にしてデプロイする
  3. ウェブアプリURL が表示されるのでコピーする

LINE BOT に Webhook URL を設定

  1. LINE developers console から作成した BOT のチャンネルに移動
  2. Messaging API設定 タブを開く
  3. Webhook設定 の設定に Apps Script をデプロイした際に発行された ウェブアプリURL を入力して保存し、Webhookの利用を ON にする
KiKiKi-KiKiKiKiKi-KiKi

GAS オウム返しをする Script の作成

message/reply API を使い、LINE bot 宛に投稿があれば、送られたメッセージを取得してそのまま返すだけのスクリプトを作成した

// ref. https://developers.line.biz/ja/reference/messaging-api/#send-reply-message
const REPLY_API = 'https://api.line.me/v2/bot/message/reply';
const ACCESS_TOKEN = 'YOUR_LINE_CHANNEL_ACCESS_TOKEN';

// LINE BOT にメッセージが合った際に実行される
function doPost(e) {
  const json = JSON.parse(e.postData.contents);

  // ref: https://developers.line.biz/ja/reference/messaging-api/#message-event
  const replyToken = json.events[0].replyToken;
  const messageText = json.events[0].message.text;

  if ( !replyToken || typeof replyToken === 'undefined' ) {
    return;
  }

  const option = {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': replyToken,
      'messages': [{
        'type': 'text',
        'text': messageText,
      }],
    }),
  };

  // POST message
  UrlFetchApp.fetch(REPLY_API, option);

  return;
}

Script を変更したら デプロイ > デプロイの管理 から新しいバージョンを作成してデプロイし直す
LINE BOT とのトークに投稿してオウム返しに返信が来ていたら OK

絵文字は (emoji) に変換されるっぽい

KiKiKi-KiKiKiKiKi-KiKi

Gemini AI に回答させる

https://gemini.google.com/

Gemini 無料でも使えるようだったので、LINE からの投稿を Gemini に回答させるようにした

1. Gemini API Key を発行する

Google AI Studio から 2024年9月22日現在 Gemini 1.5 Flash の無料で使える API キーが発行できる
https://ai.google.dev/aistudio?hl=ja

Google AI Studio にログインして Get API key をクリックすれば API キーが発行される

2. GAS から Gemini AI を呼び出す

curl \
  -H 'Content-Type: application/json' \
  -d '{"contents":[{"parts":[{"text":"Explain how AI works"}]}]}' \
  -X POST 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=YOUR_API_KEY'

Google AI Studio に curl で呼び出すサンプルが載っているので、これを GAS から fetch で呼び出せばよい

GAS に Gemini AI を呼び出す関数を追加する

const GEMINI_API_KEY = `YOUT_GEMINI_API_KEY`;
const GEMINI_API = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${GEMINI_API_KEY}`;

const askGemini = async ({ prompt }) => {
  if (!prompt || prompt.length < 3) {
    return "回答できませんでした";
  }

  const options = {
    headers: {
      "Content-Type": "application/json",
    },
    method: "post",
    payload: JSON.stringify({
      contents: [
        {
          parts: [
            {
              text: `次の質問に240文字以内で回答してください。
                質問: ${prompt}`,
            },
          ],
        },
      ],
    }),
  };

  const res = UrlFetchApp.fetch(GEMINI_API, options);
  const json = JSON.parse(res.getContentText());
  // console.log(json.candidates[0].content.parts);
  const text = json.candidates[0].content.parts[0].text;

  return text ?? "回答できませんでした";
};

LINE の投稿を質問として、askGemini 関数を呼び出す

// LINE BOT にメッセージが合った際に実行される
- function doPost(e) {
+ async function doPost(e) {
    const json = JSON.parse(e.postData.contents);

    // ref: https://developers.line.biz/ja/reference/messaging-api/#message-event
    const replyToken = json.events[0].replyToken;
    const messageText = json.events[0].message.text;

    if ( !replyToken || typeof replyToken === 'undefined' ) {
      return;
    }

+   // Ask Gemini
+   const answerText = await askGemini({
+     prompt: messageText,
+   });  
  
    const option = {
      'headers': {
        'Content-Type': 'application/json; charset=UTF-8',
        'Authorization': 'Bearer ' + ACCESS_TOKEN,
      },
      'method': 'post',
      'payload': JSON.stringify({
        'replyToken': replyToken,
        'messages': [{
          'type': 'text',
-         'text': messageText,
+         'text': answerText,
        }],
      }),
    };

    // POST message
    UrlFetchApp.fetch(REPLY_API, option);
    return;
  }

GAS を更新したら再度デプロイを行う
LINE BOT に質問をして AI から回答がされれば OK


[参考]
https://note.com/freeelover/n/n28f2c2b2779a

KiKiKi-KiKiKiKiKi-KiKi

AI が回答するまで回答中の loading を表示させる

AI の回答に時間がかかる場合があるので、返答があるまで考え中を示す loader を表示させたい
reply token の有効期間は Webhook が受信してから 1分以内なので、最大 60秒 loader を表示させれば良さそう

LINE Messaging API の loading API を利用すればOK

loading API は chatId に 会話しているユーザーのID, loadingSeconds にローディングアニメーションを表示する秒数を指定して POST すればよい。
Loader を表示する関数を作成し Gemini API を呼び出す前でその関数を呼び出すようにする

Loader を表示する関数

// ref. https://developers.line.biz/ja/reference/messaging-api/#display-a-loading-indicator
const LINE_CHAT_LOADING_API = "https://api.line.me/v2/bot/chat/loading/start";
const showLoading = ({ userID }) => {
  const option = {
    headers: {
      "Content-Type": "application/json; charset=UTF-8",
      Authorization: "Bearer " + ACCESS_TOKEN,
    },
    method: "post",
    payload: JSON.stringify({
      chatId: userID,
      loadingSeconds: 60,
    }),
  };

  UrlFetchApp.fetch(LINE_CHAT_LOADING_API, option);
};

userID を取得して showLoading 関数を呼び出す

 // LINE BOT にメッセージが合った際に実行される
 async function doPost(e) {
   const json = JSON.parse(e.postData.contents);

   // ref: https://developers.line.biz/ja/reference/messaging-api/#message-event
   const replyToken = json.events[0].replyToken;
   const messageText = json.events[0].message.text;
+  const userID = json.events[0]?.source.userId;

   if ( !replyToken || typeof replyToken === 'undefined' ) {
     return;
   }

+  // Show Loading
+  if (userID) {
+    showLoading({ userID });
+  }

   // Ask Gemini
   const answerText = await askGemini({
     prompt: messageText,
   });  
 
   const option = {
     'headers': {
       'Content-Type': 'application/json; charset=UTF-8',
       'Authorization': 'Bearer ' + ACCESS_TOKEN,
     },
     'method': 'post',
     'payload': JSON.stringify({
       'replyToken': replyToken,
       'messages': [{
         'type': 'text',
         'text': answerText,
       }],
     }),
   };

   // POST message
   UrlFetchApp.fetch(REPLY_API, option);
   return;
 }

GAS を更新して再度デプロイを行う
LINE BOT に質問をして、回答が返ってくるまでの間に 「...」 が表示されるようになっていればOK


[参考]