はじまり
あ~~、これ自動で書いてくんねえかな・・・
Gmailのやつか
届けてくれるのはありがたいんだけど、もう一つ欲しいよね
じゃあ、作るか
今回やりたいこと
Google Apps Scirptを使っていると、Gmailにエラーメッセージやその周辺の情報が送られてくることがあります。しかし、メールとして断片的になっているデータって、扱いづらいですよねえ・・・
そこで本記事では、Gmailに届くメッセージから必要な情報を抽出し、管理を効率化する方法について紹介します。
今回、こんな風に送られてくるエラー情報を・・・
スプレッドシートに書き込んでいく機能を実装していきます。
手順
それでは、実装の手順です。
1.Gmailのメッセージの取得
まずは、Gmailにあるメッセージを取得しましょう。
最初にメールのリストを取得します。ここでは、エラーメールの件名と未読かどうかでフィルタリングしています。
/**
* @description Write error log from Gamil into the sheet with GPT.
* @return {boolean}
*/
function writeErrorLogFromGmailWithGpt(){
const sheetName = SHEET_NAME_1ST;
const colOfNumber = 1;
const targetSubject = `Summary of failures for Google Apps Script: `;
let query = `label:inbox is:unread subject:"${targetSubject}"`; // 検索条件を設定
let threads = GmailApp.search(query); // Gmailのスレッドを検索
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
let startRow = 2;
let numbers = sheet.getRange(startRow, colOfNumber, 1, sheet.getLastRow()).getValues().map(record => {
return record;
});
let maxNumber = Math.max(numbers);
let i = 0;
let valuesToWrite = [];
console.log(`writeErrorLog: 222222222222222222222222222222222222222222222222222222222222222222`);
threads.forEach(function(thread) {
console.log(thread.getMessageCount()); // スレッド内のメッセージの個数
let messages = thread.getMessages(); // スレッド内のメッセージを取得
messages.forEach(function(message) {
console.log(message.getSubject()); // 使う
console.log(`writeErrorLog: 333333333333333333333333333333333333333333333333333333333333333`);
console.log(message.getDate()); // 使う
console.log(message.getPlainBody());
});
});
return true;
}
2.OpenAIのChat Completions APIを使う
次に、取得したメッセージをGPT-4に与えましょう。
まだ使ったことがなければ、以下のOpenAIのDeveloper Platformで、APIキーを作りましょう。
APIキーを作ったら、GASを書いていきます。
追加した処理の中で、GPT-4にプロンプトを与えていきます。(generatedMsg
は後で使います。)
/**
* @description Write error log from Gamil into the sheet with GPT.
* @return {boolean}
*/
function writeErrorLogFromGmailWithGpt(){
const sheetName = SHEET_NAME_1ST;
const colOfNumber = 1;
const targetSubject = `Summary of failures for Google Apps Script: `;
let query = `label:inbox is:unread subject:"${targetSubject}"`; // 検索条件を設定
let threads = GmailApp.search(query); // Gmailのスレッドを検索
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
let startRow = 2;
let numbers = sheet.getRange(startRow, colOfNumber, 1, sheet.getLastRow()).getValues().map(record => {
return record;
});
let maxNumber = Math.max(numbers);
let i = 0;
let valuesToWrite = [];
console.log(`writeErrorLog: 222222222222222222222222222222222222222222222222222222222222222222`);
threads.forEach(function(thread) {
console.log(thread.getMessageCount()); // スレッド内のメッセージの個数
let messages = thread.getMessages(); // スレッド内のメッセージを取得
messages.forEach(function(message) {
console.log(message.getSubject()); // 使う
console.log(`writeErrorLog: 333333333333333333333333333333333333333333333333333333333333333`);
console.log(message.getDate()); // 使う
console.log(message.getPlainBody());
// 追加: START ---------------------------------------------------------------------
let plainText = message.getPlainBody();
const keywords = ["function", "error_message", "trigger"];
let extractedInfo = extractErrorInfoInPlainText(plainText, keywords)
console.log(typeof extractedInfo);
console.log(extractedInfo);
console.log(extractedInfo["choices"]);
console.log(extractedInfo["choices"][0]["message"]);
let generatedMsg = extractedInfo["choices"][0]["message"]["content"];
// 追加: END ---------------------------------------------------------------------
});
});
return true;
}
それでは、extractErrorInfoInPlainText()
の中身を見ていきましょう。
この関数で、OpenAIのChat Completions APIにリクエストする内容を作ります。
/**
* @description Extract error info in plain text.
* @param {string} plainText
* @param {string[]} keywords
* @return {string{}}
*/
function extractErrorInfoInPlainText(plainText, keywords){
// 抽出した情報を格納するオブジェクト
const prompt = `この[# プレーンテキスト]から[# 欲しい情報]を[# 形式]の形式で取得してください\n\n[# プレーンテキスト]\n${plainText}\n\n[# 欲しい情報]\n- ${keywords.reduce((prev, curr) => `${prev}\n- ${curr}`)}\n\n[# 形式]\nHTMLのTable形式で、[# 欲しい情報]がヘッダーになる。`
const messages = [
{"role": "system", "content": "You are a helpful assistant."}
, {"role": "user", "content": prompt}
]
const modelName = "gpt-4";
const maxTokens = 2048;
const temperature = 0.2; // 生成する文章のランダム性(0:完全に確定的、2:完全にランダム)
let resObj = generateChatCompletionWithOpenaiApi(messages, modelName, maxTokens, temperature)
console.log(resObj);
console.log(`extractErrorInfoInPlainText: 66666666666666666666666666666666666666666666666666666666666666666666`);
return resObj;
}
/**
* @description Generate chat completions like ChatGPT.
* @param {string[]{}} messages
* @param {string} modelName
* @param {number} maxTokens
* @param {number} temperature
* @return {any{}}
*/
function generateChatCompletionWithOpenaiApi(messages, modelName="gpt-4", maxTokens=2048, temperature=1){
const endpoint = "https://api.openai.com/v1/chat/completions";
const apiKey = API_KEY_OPENAI;
const payload = {
"messages": messages
, "model": modelName
, "max_tokens": maxTokens
, "temperature": temperature
};
const options = {
"method": "POST"
, "headers": {
"Content-Type": "application/json"
, "Authorization": "Bearer " + apiKey
}
, "payload": JSON.stringify(payload)
};
const res = UrlFetchApp.fetch(endpoint, options);
return JSON.parse(res.getContentText());
}
この関数の中の、prompt変数にはプロンプトしたい文章が入っています。
ChatGPTで入力してみるとこんな感じ。
この[# プレーンテキスト]から[# 欲しい情報]を[# 形式]の形式で取得してください
[# プレーンテキスト]
スクリプト TestFunctionSheet を正常に完了できませんでした。失敗のまとめを下記
に示します。このスクリプトのトリガーを設定したり、今後の失敗の通知の受信設定
を変更したりするには、ここをクリックしてください。
起動 関数 エラー メッセージ トリガー End
2024/02/18 5時00分55秒 日本標準時 testfunction TypeError:
statement.replace is not a function time-based 2024/02/18 5時01分15秒 日本標
準時
よろしくお願いいたします。
Google Apps Script
サポートが必要な場合は、Google Apps Script のドキュメントをご覧ください。本
メールは配信専用です。ご返信なさらぬようご注意ください。(c) 2024 Google
[# 欲しい情報]
- function
- error_message
- trigger
[# 形式]
HTMLのTable形式で、[# 欲しい情報]がヘッダーになる。
GASを実行してみて、こんなエラーメッセージが表示されたら、おそらくクレジットが0になっている可能性があるので、さっきのDeveloper Platformでクレジットカードを登録しましょう。。。(5ドルくらい払えばとりあえず問題ないと思います。)
"error": {
"message": "The model `gpt-4` does not exist or you do not have access to it. Learn more: https://help.openai.com/en/artic... (use muteHttpExceptions option to examine full response)
3.Parserで欲しい情報を抽出する
次に、GPT-4に生成してもらった内容を切り抜いていきましょう。
今回は、GASのParserライブラリを使っていきます。スクリプトのライブラリに入れていきましょう。
(IDは、1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw
)
そして、情報を抽出していきます。このために、GPT-4にはHTML形式で情報を出してもらったのでした。
/**
* @description Write error log from Gamil into the sheet with GPT.
* @return {boolean}
*/
function writeErrorLogFromGmailWithGpt(){
const sheetName = SHEET_NAME_1ST;
const colOfNumber = 1;
const targetSubject = `Summary of failures for Google Apps Script: `;
let query = `label:inbox is:unread subject:"${targetSubject}"`; // 検索条件を設定
let threads = GmailApp.search(query); // Gmailのスレッドを検索
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
let startRow = 2;
let numbers = sheet.getRange(startRow, colOfNumber, 1, sheet.getLastRow()).getValues().map(record => {
return record;
});
let maxNumber = Math.max(numbers);
let i = 0;
let valuesToWrite = [];
console.log(`writeErrorLog: 222222222222222222222222222222222222222222222222222222222222222222`);
threads.forEach(function(thread) {
console.log(thread.getMessageCount()); // スレッド内のメッセージの個数
let messages = thread.getMessages(); // スレッド内のメッセージを取得
messages.forEach(function(message) {
console.log(message.getSubject()); // 使う
console.log(`writeErrorLog: 333333333333333333333333333333333333333333333333333333333333333`);
console.log(message.getDate()); // 使う
console.log(message.getPlainBody());
let plainText = message.getPlainBody();
const keywords = ["function", "error_message", "trigger"];
let extractedInfo = extractErrorInfoInPlainText(plainText, keywords)
console.log(typeof extractedInfo);
console.log(extractedInfo);
console.log(extractedInfo["choices"]);
console.log(extractedInfo["choices"][0]["message"]);
let generatedMsg = extractedInfo["choices"][0]["message"]["content"];
// 追加: START ---------------------------------------------------------------------
let extractedErrorInfo = extractErrorInfoFromChatGptMsg(generatedMsg);
// 追加: END ---------------------------------------------------------------------
});
});
return true;
}
それでは、extractErrorInfoFromChatGptMsg()
の中身を見ていきましょう。
/**
* @description Extract Error info from messages ChatGPT-4 generated.
* @param {string} message
* @return {string[]}
*/
function extractErrorInfoFromChatGptMsg(message){
const markToParse1 = "th";
const markToParse2 = "td";
let texts1 = Parser.data(message).from(`<${markToParse1}>`).to(`</${markToParse1}>`).iterate();
let texts2 = Parser.data(message).from(`<${markToParse2}>`).to(`</${markToParse2}>`).iterate();
console.log(texts1);
console.log(texts2);
console.log(`extractErrorInfoFromChatGptMsg: 11111111111111111111111111111111111111111111111111111111111111`);
let returnObj = {};
returnObj["is_table_deformed"] = false;
if(texts1.length !== texts2.length || texts2.length !== 3){
console.log(`extractErrorInfoFromChatGptMsg: 22222222222222222222222222222222222222222222222222222222222222222`);
returnObj["is_table_deformed"] = true;
}
let returns = [];
for(let i = 0; i < texts2.length; i++){
returns.push(texts2[i].replaceAll(" ","_"));
}
returnObj["info_list"] = returns;
console.log(`extractErrorInfoFromChatGptMsg: 99999999999999999999999999999999999999999999999999999999999999`);
return returnObj;
}
すると、returnObj["info_list"]
の中に、必要な情報が入った配列が入ります。
[ 'testfunction', 'TypeError: statement.replace is not a function', 'time-based' ]
4.スプレッドシートに記録する
書き込みたい情報は取れたので、シートに書きます。
/**
* @description Write error log from Gamil into the sheet with GPT.
* @return {boolean}
*/
function writeErrorLogFromGmailWithGpt(){
const sheetName = SHEET_NAME_1ST;
const colOfNumber = 1;
const targetSubject = `Summary of failures for Google Apps Script: `;
let query = `label:inbox is:unread subject:"${targetSubject}"`; // 検索条件を設定
let threads = GmailApp.search(query); // Gmailのスレッドを検索
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
let startRow = 2;
let numbers = sheet.getRange(startRow, colOfNumber, 1, sheet.getLastRow()).getValues().map(record => {
return record;
});
let maxNumber = Math.max(numbers);
let i = 0;
let valuesToWrite = [];
console.log(`writeErrorLog: 222222222222222222222222222222222222222222222222222222222222222222`);
threads.forEach(function(thread) {
console.log(thread.getMessageCount()); // スレッド内のメッセージの個数
let messages = thread.getMessages(); // スレッド内のメッセージを取得
messages.forEach(function(message) {
console.log(message.getSubject()); // 使う
console.log(`writeErrorLog: 333333333333333333333333333333333333333333333333333333333333333`);
console.log(message.getDate()); // 使う
console.log(message.getPlainBody());
let plainText = message.getPlainBody();
const keywords = ["function", "error_message", "trigger"];
let extractedInfo = extractErrorInfoInPlainText(plainText, keywords)
console.log(typeof extractedInfo);
console.log(extractedInfo);
console.log(extractedInfo["choices"]);
console.log(extractedInfo["choices"][0]["message"]);
let generatedMsg = extractedInfo["choices"][0]["message"]["content"];
let extractedErrorInfo = extractErrorInfoFromChatGptMsg(generatedMsg);
// 追加: START ---------------------------------------------------------------------
valuesToWrite.push([
maxNumber + i
, generateFormulaForConId(colOfNumber, maxNumber + i + 1)
, message.getDate()
, message.getDate()
, message.getSubject().replace(targetSubject, "")
, extractedErrorInfo["info_list"][0]
, extractedErrorInfo["info_list"][1]
, extractedErrorInfo["info_list"][2]
]);
message.markRead();
console.log(message.isUnread());
console.log(`writeErrorLog: 6666666666666666666666666666666666666666666666666666666666666666666`);
// 追加: END ---------------------------------------------------------------------
});
});
// 追加: START ---------------------------------------------------------------------
console.log(valuesToWrite);
console.log(`writeErrorLog: 777777777777777777777777777777777777777777777777777777777777777777`);
sheet.getRange(maxNumber + 2, colOfNumber, valuesToWrite.length, valuesToWrite[0].length).setValues(valuesToWrite);
// 追加: END ---------------------------------------------------------------------
return true;
}
そしたら、こんな風にシートに書き込まれます。
5.運用コストがさぁ・・・
これでツールは完成!
・・・ですが、実装する過程で7回くらいChat Completions APIにリクエストしたわけですが、
その直後にDeveloper Platformで消費したクレジットの額面を見てみると・・・
たったの7リクエストで18円も掛かっている・・・
例えば、1日に3通のエラーメッセージをシートに記録するとなると、3 × 365 × 18 ÷ 7≒2815円も年額として掛かりますよ!?
うっ、うひゃあ・・・。
これは実際に運用するかどうかというのは、別問題になってくるぞ・・・
まとめ
今回は、以下の流れでGmailの内容をGPT-4で抽出する処理を実装しました。
- Gmailのメッセージを取得する
- OpenAIのChat Completions APIを使う
- Parserで欲しい情報を抽出する
- スプレッドシートに記録する
Gmailに届くメッセージから必要な情報を効率的に抽出することは、日々の業務効率化に大きく寄与します。Gmail APIとOpenAI APIを組み合わせることで、メール管理の自動化が可能となり、重要な情報を見逃すリスクを減らすことができるでしょう。
しかし、お財布と相談するのを忘れずに。
おしまい
記録できたぞ
財布的に、年がら年中は使えんかもなあ・・・
以上になります!
コメント