具材を入力するだけでレシピ完成!「AI献立くん」
この度、「AI献立くん」をリリースしました!
AI献立くん : https://lin.ee/sRdqjeO
具材を何個か入力するだけで、自動で何個か献立を考えてくれます。
今回のリリースでは、下記の3種類のレシピを考えてくれます。
- すぐに作れるもの
- 簡単に作れるもの
- 美味しく作れるもの
同時にYouTubeでもリリース動画を出しています。
作り方
今回はLINEの公式アカウントをGoogle Apps Script (GAS) でスプレッドシートをデータベースに実装しています。AI部分はChatGPTのAIである「gpt-3.5-turbo」を使用しています。
詳しいGASでAI搭載のLINE公式アカウントの作り方に関しては、Udemy講座をご確認ください。
Udemy講座 : https://www.udemy.com/course/chatgpt-line-function-calling-ai/
今回リリースしているプログラム部分に関しては、下記に公開しておきます。
スプレッドシートは「制約」「ログ」「ユーザー」の3種類を用意しています。
コード.gs
function doPost(e) { // メッセージ受信 const data = JSON.parse(e.postData.contents).events[0] // ユーザーID取得 const lineUserId = data.source.userId // リプレイトークン取得 const replyToken = data.replyToken // 送信されたメッセージ取得 const postMessage = data.message.text // データのタイプを取得 const dataType = data.type // フォロー時はユーザーデータを登録 if (dataType == "follow") addUser(userId) // 個別のスプレッドシート情報取得 // const SHEET_ID = findSheetId(lineUserId); // テキスト以外だった時(スタンプや写真など) if (postMessage === undefined) return sendMessage(replyToken, "献立を作るAIなので、お手元にある具材を伝えてね!\n例:ソーセージ、ポテト、人参"); // データ生成&LINEに送信 const replyText = chatGPT(postMessage); sendMessage(replyToken, replyText); debugLog(lineUserId, postMessage, replyText); } // 返答するメッセージを生成 function chatGPT(prompt) { // prompt = "こんにちは" let constraints; try { constraints = SHEET.getRange(1, 1).getValue() } catch (e) { console.log("制約なし"); } const requestOptions = { "method": "post", "headers": { "Content-Type": "application/json", "Authorization": "Bearer "+ OPENAI_APIKEY }, "payload": JSON.stringify({ "model": CHAT_GPT_VER, "messages": [ {"role": "system", "content": constraints}, {"role": "user", "content": prompt} ] }) } const response = UrlFetchApp.fetch(CHAT_GPT_URL, requestOptions); const responseText = response.getContentText(); const json = JSON.parse(responseText); const text = json['choices'][0]['message']['content'].trim(); console.log(text); const search_prompt = prompt.replace(/ /g, 'と').replace(/ /g, 'と').replace(/\n/g, 'と'); const return_text = `${text}\n\nhttps://cookpad.com/search/${search_prompt}` return (return_text); } // LINEに返答するデータ生成 function sendMessage(replyToken, replyText) { const postData = { "replyToken" : replyToken, "messages" : [ { "type" : "text", "text" : replyText } ] }; return postMessage(postData); } // LINEに返答 function postMessage(postData) { const headers = { "Content-Type" : "application/json; charset=UTF-8", "Authorization" : "Bearer " + LINE_ACCESS_TOKEN }; const options = { "method" : "POST", "headers" : headers, "payload" : JSON.stringify(postData) }; return UrlFetchApp.fetch(LINE_REPLY_URL, options); } ろ
ログ.gs
function debugLog(userId, text, replyText) { if ('' == text) return; // テキストではない時はログなし const UserData = findUser(userId); // ユーザーシートにデータがあるか確認 typeof UserData === "undefined" ? addUser(userId) : userUseChat(userId); // ユーザーシートにデータがなければユーザー追加、あれば投稿数だけ追加 const date = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss'); // 現在の日付を取得 SHEET_LOG.appendRow([userId, UserData, text, replyText, date]); // ログシートに情報追加 // 日付順に並び替え const numColumn_LOG = SHEET_LOG.getLastColumn(); // 最後列の列番号を取得 const numRow_LOG = SHEET_LOG.getLastRow()-1; // 最後行の行番号を取得 const dataRange_LOG = SHEET_LOG.getRange(2, 1, numRow_LOG, numColumn_LOG); dataRange_LOG.sort([{column: 5, ascending: false}]); // 日付順に並び替え } // メンバーとしてユーザー登録されているか検索 function findUser(uid) { return getUserData().reduce(function(uuid, row) { return uuid || (row.key === uid && row.value); }, false) || undefined; } // ユーザー情報取得 function getUserData() { const data = SHEET_USER.getDataRange().getValues(); return data.map(function(row) { return {key: row[0], value: row[1]}; }); } // ユーザー追加 function addUser(userId) { const userName = getUserDisplayName(userId) const userIMG = getUserDisplayIMG(userId) const SHEET_ID = sheetCopy(userName) const DATE = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss'); // 現在の日付を取得 SHEET_USER.appendRow([userId, userName, userIMG, 0, SHEET_ID, DATE, DATE]) } // シートをコピー function sheetCopy(userName) { // そのスプレッドシートのコピーを作成 const SS_COPY = SS.copy(`[${userName}] ${SS.getName()}`) // そのスプレッドシートのIDを取得 const SHEET_ID = SS_COPY.getId() // コピーしたシートのユーザー情報部分は削除 const SHEET_LOG = SS_COPY.getSheetByName('ログ') SHEET_LOG.deleteColumns(1,2) const SHEET_USER = SS_COPY.getSheetByName('ユーザー') SS_COPY.deleteSheet(SHEET_USER) // シートのログデータは全て削除 const numColumn = SHEET_LOG.getLastColumn() // 最後列の列番号を取得 const numRow = SHEET_LOG.getLastRow()-1 // 最後行の行番号を取得 if (numRow != 0) { SHEET_LOG.getRange(2, 1, numRow, numColumn).clear() } return SHEET_ID } // ユーザーのプロフィール名取得 function getUserDisplayName(userId) { const url = 'https://api.line.me/v2/bot/profile/' + userId; const userProfile = UrlFetchApp.fetch(url,{ 'headers': { 'Authorization' : 'Bearer ' + LINE_ACCESS_TOKEN, }, }) return JSON.parse(userProfile).displayName; } // ユーザーのプロフィール画像取得 /////////////////////////////// function getUserDisplayIMG(userId) { const url = 'https://api.line.me/v2/bot/profile/' + userId; const userProfile = UrlFetchApp.fetch(url,{ 'headers': { 'Authorization' : 'Bearer ' + LINE_ACCESS_TOKEN, }, }) return JSON.parse(userProfile).pictureUrl; } // ユーザーの投稿数をプラス1 function userUseChat(userId) { // 送信したユーザー先のユーザーを検索 const textFinder = SHEET_USER.createTextFinder(userId); const ranges = textFinder.findAll(); // ユーザーが存在しない場合エラー if(!ranges[0]) SHEET_USER.appendRow([userId, "???", '', 1]);; // ステータスを出勤中から離席中に変更 const statusFinder = SHEET_USER.createTextFinder('投稿数'); const statusRanges = statusFinder.findAll(); const row = ranges[0].getRow(); const column = statusRanges[0].getColumn(); // 投稿数追加 let number = SHEET_USER.getRange(row, column).getValue(); SHEET_USER.getRange(row, column).setValue(++number); }
config.gs
// API設定部分 const OPENAI_APIKEY = "sk-*******************************************"; const LINE_ACCESS_TOKEN = "**************************************************************************************************************************************************************************="; // 使用API const LINE_REPLY_URL = 'https://api.line.me/v2/bot/message/reply'; const CHAT_GPT_URL = "https://api.openai.com/v1/chat/completions"; const CHAT_GPT_VER = "gpt-3.5-turbo"; // スプレッドシートの情報 const SS = SpreadsheetApp.getActiveSpreadsheet(); const SHEET = SS.getSheetByName('制約'); const SHEET_LOG = SS.getSheetByName('ログ'); const SHEET_USER = SS.getSheetByName('ユーザー');さ
さいごに
今後、毎月何かしら新しいサービスをリリースしていこうと思っています。今回は1時間くらいで作れたので、毎日新しいサービスを作ろうと思えば作れるんじゃないかとも思いますが、長く続けていくことのほうが大事だと思っているので、毎月に設定しています。
暖かく見守っていただけると嬉しいです。サービス開発で億り人になるぞっ!
Udemy講座もよろしくお願いします!
Udemy講座 : https://www.udemy.com/course/chatgpt-line-function-calling-ai/