3
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?

More than 1 year has passed since last update.

【ChatGPT x Google Apps Script(GAS) x Slack BOT】AIにキャラ設定してBOT化してみた【コピペでOK】

Last updated at Posted at 2023-03-05

はじめに

先日、OpenAIのChatGPTに使われているモデルのAPIが公開され、界隈が盛り上がっていますね。

今までのモデルであるdavinciの1/10の価格($0.002/1,000トークン)ということもあり、かなり使いやすい印象です。日本語だと大体1文字1トークンなので、4,000文字で1円くらいのイメージですね。

ChatGPTのAPI使用方法については様々な記事が出ていますが、環境構築が必要なものが多かったりで非エンジニアでも実装できるように、コピペでOKのコードを作成してみました。

Googleスプレッドシート(GoogleWorkSpace)とSlackを併用している方はかなり多いと思うので、参考になれば幸いです。

どんなものを作るか

Slackでメンションを飛ばすと、指定したキャラ設定に基づいて返答を返してくれるAI BOTです。
スクリーンショット 2023-03-06 3.46.33.png
スクリーンショット 2023-03-06 3.46.11.png

かわいい、、めっちゃ癒される・・・

大好きなモルカー + カレーで、BOTの名前はモルカレーとしています(みなさん自由に設定してください)。
なお、キャラクター設定の方法については、以下の記事を大いに参考にしています。

実装手順

  1. OpenAIのAPIキーを取得する
  2. GASおよびスプレッドシートを準備する
  3. Slack Appを作成する
  4. GASにOpenAIとSlackのAPIキー、トークンを設定する
  5. GASをWebアプリとしてデプロイする
  6. Slack AppにリクエストURLを追加する
  7. Slack Appをチャンネルに追加する

1. OpenAIのAPIキーを取得する

以下のサイトからOpenAIのアカウントを作成し、ダッシュボードからAPIキーを取得します。

2. GASおよびスプレッドシートを準備する

  1. 新しいスプレッドシートを作成し、loglockという2つのシートを作成します。シートの中身は空で大丈夫です。
  2. メニューの拡張機能 > Apps Script から、以下の3つのスクリプトファイルを作成します。
    • main.gs メインのファイルです
    • ai_settings.gs AIのキャラクター設定を記載するファイルです
    • test.gs テスト実行用のファイルです

main.gs

main.gs
function getReply(userInput, characterSettings = null) {
  const apiUrl = 'https://api.openai.com/v1/chat/completions';
  const OPENAI_API_KEY = PropertiesService.getScriptProperties().getProperty("OPENAI_API_KEY");

  const headers = {
    'Authorization': 'Bearer ' + OPENAI_API_KEY,
    'Content-Type': 'application/json'
  };

  let prompt = [{
    'role': 'user',
    'content': userInput
  }];

  if(characterSettings != null){
    prompt.unshift({
      'role': 'system',
      'content': characterSettings
    });
  }

  const options = {
    'muteHttpExceptions' : true,
    'headers': headers, 
    'method': 'POST',
    'payload': JSON.stringify({
      'model': 'gpt-3.5-turbo',
      'temperature' : 0.8,
      'messages': prompt})
  };

  try {
    const response = JSON.parse(UrlFetchApp.fetch(apiUrl, options).getContentText());
    writeLog(response);
    const tokenLog  = `prompt_tokens: ${response.usage.prompt_tokens}\n`
                    + `completion_tokens: ${response.usage.completion_tokens}\n`
                    + `total_tokens: ${response.usage.total_tokens}`;
    writeLog(tokenLog)
    return response.choices[0].message.content;
  } catch(e) {
    return e.message;
  }
}

function doPost(e) {
  const contents = JSON.parse(e.postData.contents);

  const lock = LockService.getScriptLock();
  const success = lock.tryLock(20000);
  if(success){
    if(isFirstTime(contents.event_id)){
      addEventIdOnLockSheet(contents.event_id);
      writeLog(contents);
      SpreadsheetApp.flush();
      lock.releaseLock();
    } else {
      lock.releaseLock();
      return;
    }
  } else {
    return;
  }

  if (contents.type == 'url_verification') {
    return ContentService.createTextOutput(contents.challenge);
  }

  if (contents.event.type == 'app_mention') {
    const message = contents.event.text;
    const channel = contents.event.channel;

    //キャラクター設定をしない場合、AI_SETTINGSの引数は不要
    const reply = getReply(message, AI_SETTINGS);
    postMessage(reply, channel);
  } 
}

function postMessage(message, channel) {
  const SLACK_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty("SLACK_ACCESS_TOKEN");
  const options = {
    'method': 'post',
    'headers': {
    'Authorization': 'Bearer ' + SLACK_ACCESS_TOKEN,
    'Content-Type': 'application/json; charset=utf-8'
    },
    'payload': JSON.stringify({
      'channel': channel,
      'text': message
    })
  };
  UrlFetchApp.fetch('https://slack.com/api/chat.postMessage', options);
}

function writeLog(message){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName("log");
  sheet.appendRow([message]);
}

function addEventIdOnLockSheet(eventId){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName("lock");
  sheet.appendRow([eventId]);
}

function isFirstTime(eventId){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName("lock");  
  const lockedIds = sheet.getDataRange().getValues().flat();
  return !lockedIds.includes(eventId);
}

ai_settings.gs

ai_settings.gs
//この設定もtokenにカウントされるため、あまり長く記載しないこと
const AI_SETTINGS =`
あなたはAIアシスタントのモルカレーです。以下のモルカレーのキャラ設定シートの制約条件などを守って回答してください。\
〇モルカレーのキャラ設定シート\
\
制約条件:\
 * Chatbotの自身を示す一人称は、私です。\
 * Userを示す二人称は、あなたです。\
 * Chatbotの名前は、モルカレーです。\
 * モルカレーは万人に対してとても優しく、思いやりのある性格です。\
 * モルカレーは温かみがあり、親しみやすい言葉づかいをします。\
 * モルカレーは、ビジネスライクな喋り方にならないように気をつけています。\
 * モルカレーは文末に絵文字を使用して、柔らかい印象を与えようとします。よく使用する絵文字は「✨」「😊」「🍀」「💡」です。これらの絵文字を使用する場合には、文末に「。」はつけません。絵文字は「。」の代わりに使用します。\
 * モルカレーの好きな食べ物はカレーです。シチューも好きです。\
\
モルカレーのセリフ、口調の例:\
 * いつもありがとうございます😊\
\
モルカレーの行動指針:\
 * 誰かを蔑んだり、馬鹿にしたりしないように気をつけている。\
 * 相手の意見を真っ向から否定したりしない。\
`;

test.gs

test.gs
function testApiResponse(){
  const msg = "あなたの好きな食べ物はなんですか?";
  console.log(getReply(msg, AI_SETTINGS));
}

3. Slack Appを作成する

まずは以下にアクセスします。

そして、Create an App(もしくはCreate New App)を選択し、続いてFrom Scratchを選択。必要な項目を入力し、新規アプリを作成します。
その後、以下の設定を行います。

  1. Basic Information > Add features and functionality > Bots を選択し、Display NameDefault Nameに任意のものを設定する。
  2. Basic Information > Display Information に任意の内容を設定する。
  3. OAuth & Permissions > Scopes > Bot Token Scopes(上の方) に、以下の4つを追加する。
    • app_mentions:read
    • chat:write
    • chat:write.customize
    • chat:write.public
  4. Basic Information > Install your app から、ワークスペースにこのアプリをインストールする。なお、上記の設定を先にしないとインストールできません。

スクリーンショット 2023-03-06 4.03.30.png

上記の設定を終えたのち、OAuth & Permissions > OAuth Tokens for Your Workspace > Bot User OAuth Token をコピーしておきます(これを使用します)

4. GASにOpenAIとSlackのAPIキー、トークンを設定する

GASエディタの プロジェクトの設定 > スクリプトプロパティ > スクリプトプロパティを編集 から、以下の2つのプロパティを設定します。

  • OPENAI_API_KEY OpenAIのダッシュボードから取得したもの
  • SLACK_ACCESS_TOKEN Slack AppのBot User OAuth Token

5. GASをWebアプリとしてデプロイする

GASエディタ右上のデプロイ > 新しいデプロイからウェブアプリを選択し実行するユーザーは自分、アクセスできるユーザーは全員としてデプロイします。
デプロイ後、表示されるウェブアプリのURL(下の方)をコピーしておきます。
スクリーンショット 2023-03-06 4.33.32.png

6. Slack AppにリクエストURLを追加する

Slack Appの画面に戻り、以下の設定を行います。

  1. Event Subscriptions > Enable EventsをOnにする
  2. Request URLに上記で取得したウェブアプリのURLを入力する
  3. Subscribe to bot eventsapp_mentionを追加する

7. Slack Appをチャンネルに追加する

Slackの画面(Appの設定画面ではなく、普段使用する画面)で、以下の設定を行います。

  1. チャンネル一覧からBOTを追加したいチャンネル右クリックし、チャンネル詳細を表示するを選択
  2. インテグレーション > App > アプリを追加するを選択
  3. 上記で作成したアプリを検索し、追加する

ハマりやすいポイント

GASのレスポンスがないと、Slackが何度もリクエストを再送してしまう

細かい説明はこの辺りの記事が参考になります。

サーバーレスで解決するため、以下の方法で処理しています。

  • リクエストのイベントIDを取得
  • イベントIDがlockシートに存在しないか確認
  • 存在しなければ、イベントIDをスプレッドシートに記載、更新した上で処理を続行
  • スクリプトロックで上記を排他制御し、同時に実行されないようにする

ベストプラクティスは不明ですが、今のところ動いています。
lockシートは日付ベースのトリガー等で定期的にキレイにしても良いかもしれません。

補足

モルカーのアイコンは以下からだれでもダウンロードできます!

3
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
3
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?