![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-12-1024x143.png)
概要
- ChatworkのbotとしてchatGPTを活用する
- 文脈を保持して会話ができるようになる
前提
- OpenAIのアカウントを持っている
- Googleのアカウントを持っている
- GASのアカウントとスプレッドシートのアカウントは同じアカウントを使う
- Chatworkのアカウントを持っている
始めましょう
000.各種トークンを発行及び準備
- Chatwork APIトークン
- OpenAI
- Chatworkに今回のbot用アカウントを開設(無料でOK)
- googleスプレッドシートに会話保存用の空のシートを作る
001.Google APP Scriptに新しいプロジェクトを作る
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-1024x263.png)
002.プロジェクト名を適当につける
変更する必要もないけど、後からわかるように「無題のプロジェクト」「コード.gs」を変更した方がいいでしょう。ネーミングに決まりはないです。
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-1-1024x518.png)
003.ChatworkClientのライブラリを追加
下記のコードでライブラリを検索できます。
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-13-1024x734.png)
1nf253qsOnZ-RcdcFu1Y2v4pGwTuuDxN5EbuvKEZprBWg764tjwA5fLav
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-2-1024x979.png)
004.下記のコードをGoogle APP Scriptにコピペ!
注意:
// シート毎の会話履歴の最大件数を設定<br>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.デプロイ!
種類の選択は「ウェブアプリ」
新しい説明文は任意で適当に!あとは画像の通りに。
一発目のデプロイではアクセス権をを求められるので「アクセスを承認」に進んでください。
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-3-1024x806.png)
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-4-1024x805.png)
006.URLをコピー
デプロイが完了した画面に出てくるURLをコピーしといてください。
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-14-1024x809.png)
007.ChatworkでWebhookを新規作成
Chatbot専用のアカウントでchatworkにログインして作成してください。
Webhook URLには先ほどコピーしたGoogle APP ScriptのウェブアプリのURLを貼り付け
Webhook名は適当。あとは画像の通りに。
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-8-1024x716.png)
保存したら完成です。
使い方
本来のChatworkアカウントとコンタクト追加・招待などで繋がればあとは普通に話すだけです。
グループでは toでbotを指定すると話します。
ちょっとした遊び
スプレッドシートに会話の履歴が保存されますが、データの肥大化を防ぐために「HISTORY_LIMIT」で保持件数を制御して古いデータから消していきます。
ただし1行目だけは消しません。
なので、スプレッドシートにアクセスして直接1行目を変更する事で簡単な特性を持たせる事ができます。
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-9-1024x452.png)
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-10-1024x495.png)
![](https://gentleman-loser.com/xg/wp-content/uploads/2024/02/image-11-1024x217.png)