チャットワークとchatGPTをGAS(Google APP Script)で連携した方法

チャットワークとchatGPTをGAS(Google APP Script)で連携した方法

概要

ChatworkのbotとしてchatGPTを活用する
文脈を保持して会話ができるようになる

前提

始めましょう

000.各種トークンを発行及び準備

  • Chatwork APIトークン
  • OpenAI
  • Chatworkに今回のbot用アカウントを開設(無料でOK)
  • googleスプレッドシートに会話保存用の空のシートを作る

001.Google APP Scriptに新しいプロジェクトを作る

Google App Scriptにアクセス

002.プロジェクト名を適当につける

変更する必要もないけど、後からわかるように「無題のプロジェクト」「コード.gs」を変更した方がいいでしょう。ネーミングに決まりはないです。

003.ChatworkClientのライブラリを追加

下記のコードでライブラリを検索できます。

1nf253qsOnZ-RcdcFu1Y2v4pGwTuuDxN5EbuvKEZprBWg764tjwA5fLav

004.下記のコードをGoogle APP Scriptにコピペ!

スプレッドシートのIDの見つけ方

注意:

// シート毎の会話履歴の最大件数を設定
const HISTORY_LIMIT = 41; // 件数

この部分のリミット数を上げるとTOKENの使用量も増えるっぽいので気をつけた方が良い。
件数というよりスプレッドシート記録の行数なので、一会話2行となる。
最初の1行目は削除されないようにしてるので、41 に設定した場合は20回の会話を保持して古い行から2行ずつ(2行目から)削除する仕様になってます。

// グローバル変数の定義
const TOKEN = 'Chatwork APIトークン'; // Chatwork APIトークン
const SECRET_KEY = "OpenAI APIキー"; // OpenAI APIキー
const MODEL_NAME = "gpt-4-1106-preview"; // 使用するモデル名
const MODEL_TEMP = 0.5; // モデルの温度設定
const MAX_TOKENS = 100; // 最大トークン数

// スプレッドシートのID(スプレッドシートのURLから取得できる)
const SPREADSHEET_ID = 'スプレッドシートのID';
// シート毎の会話履歴の最大件数を設定
const HISTORY_LIMIT = 41; // 件数

/**
 * ここから下は変更不要
 */

function doPost(e) {
    let json = JSON.parse(e.postData.contents);
    let room_id = json.webhook_event.room_id;
    let body = json.webhook_event.body;

    callChatCompletionsAPI(room_id, body, function(responseMessage) {
        sendChatworkMessage(room_id, responseMessage);
        saveConversationHistory(room_id, [{role: "user", content: body}, {role: "assistant", content: responseMessage}]);
    });
}

function getConversationHistory(room_id) {
    try {
        const sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(room_id.toString());
        if (!sheet) return null; // シートが存在しない場合はnullを返す
        const data = sheet.getDataRange().getValues();
        if (data.length === 0) return null; // シートが空の場合はnullを返す
        return data.map(row => ({role: row[0], content: row[1]}));
    } catch (e) {
        console.error("Error in getConversationHistory: ", e.toString());
        return null; // エラーが発生した場合はnullを返す
    }
}

function callChatCompletionsAPI(room_id, prompt, callback) {
    let conversationHistory = getConversationHistory(room_id) || [];
    
    // 新しいグループでの最初の会話の場合、適切な初期化を行う
    if (conversationHistory.length === 0) {
        conversationHistory.push({role: "system", content: "会話を開始します。"});
    }
    
    conversationHistory.push({role: "user", content: prompt});

    const payload = {
        model: MODEL_NAME,
        messages: conversationHistory,
        temperature: MODEL_TEMP, // モデルの温度設定を追加
        max_tokens: MAX_TOKENS, // 最大トークン数を追加
    };

    const options = {
        method: 'POST',
        contentType: 'application/json',
        headers: {
            'Authorization': 'Bearer ' + SECRET_KEY,
            'Content-Type': 'application/json'
        },
        payload: JSON.stringify(payload),
        muteHttpExceptions: true
    };


    try {
        const response = UrlFetchApp.fetch(url, options);
        const res = JSON.parse(response.getContentText());
        if (response.getResponseCode() == 200) {
            const responseMessage = res.choices[0].message.content.trim();
            callback(responseMessage);
        } else {
            console.error("API Error: ", res.error.message);
            callback("APIエラー: " + res.error.message);
        }
    } catch (e) {
        console.error("Error fetching OpenAI API: ", e.toString());
        callback("API呼び出し中にエラーが発生しました: " + e.toString());
    }
}


function sendChatworkMessage(room_id, message) {
    let client = ChatWorkClient.factory({token: TOKEN});
    let formattedMessage = `[info]${message}[/info]`;
    client.sendMessage({
        room_id: room_id,
        body: formattedMessage
    });
}

/**
 * スプレッドシートに会話履歴を保存
 */
function saveConversationHistory(room_id, messages) {
    console.log("Saving conversation history for room_id:", room_id, "with messages:", messages);
    try {
        let sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(room_id.toString());
        if (!sheet) {
            console.log("Sheet not found, creating a new one for room_id:", room_id);
            sheet = SpreadsheetApp.openById(SPREADSHEET_ID).insertSheet(room_id.toString());
            // 初期メッセージ行を追加
            sheet.appendRow(["system", "会話を開始します。"]);
        }

        // 新しいメッセージを追加
        messages.forEach(message => sheet.appendRow([message.role, message.content]));

        // 既存の履歴と新しいメッセージを追加後の行数を取得
        let totalRows = sheet.getLastRow();
        // 履歴がリミットを超えているか確認し、超えていれば最も古いメッセージを削除
        if (totalRows > HISTORY_LIMIT + 1) { // +1 はシステムメッセージの行
            let rowsToDelete = totalRows - (HISTORY_LIMIT + 1);
            sheet.deleteRows(2, rowsToDelete); // 2行目から削除を開始
        }

        console.log("History updated successfully.");
    } catch (e) {
        console.error("Error updating conversation history:", e.toString());
    }
}

005.デプロイ!

種類の選択は「ウェブアプリ」
新しい説明文は任意で適当に!あとは画像の通りに。

一発目のデプロイではアクセス権をを求められるので「アクセスを承認」に進んでください。

006.URLをコピー

デプロイが完了した画面に出てくるURLをコピーしといてください。

007.ChatworkでWebhookを新規作成

Chatbot専用のアカウントでchatworkにログインして作成してください。

Webhook URLには先ほどコピーしたGoogle APP ScriptのウェブアプリのURLを貼り付け

Webhookの設定画面への行き方

Webhook名は適当。あとは画像の通りに。

保存したら完成です。

使い方

本来のChatworkアカウントとコンタクト追加・招待などで繋がればあとは普通に話すだけです。

グループでは toでbotを指定すると話します。

ちょっとした遊び

スプレッドシートに会話の履歴が保存されますが、データの肥大化を防ぐために「HISTORY_LIMIT」で保持件数を制御して古いデータから消していきます。
ただし1行目だけは消しません。

なので、スプレッドシートにアクセスして直接1行目を変更する事で簡単な特性を持たせる事ができます。

これが初期状態
知らんけど って言うように変更してみる

独り言カテゴリの最新記事