トークグループに対しての通知はあまり気にしたことがなかったので、ちょうど別件で作っていたサンプルがあったのでそれを元に。Googleドライブにある画像ファイルをランダムにピックアップし、選ばれた1枚をトークグループに通知してくれます。
渾身の1枚を激写 => 1枚激写 => 一激ということで、定期的にグループに対して通知すれば話が弾むのではないでしょうか!
あと、せっかくなのでChatGPTを絡めて、「画像から得られる教訓を大喜利風」にコメントしてもらおうと思います。
本記事は下記アドベントカレンダーの記事でもあります。
必要なもの
- LINE Developers Consoleアカウント
- OpenAIアカウント(gpt-4-vision-preview利用)
- 画像認識を使うには、Usage tier 1担っている必要があります(おそらく)
- Googelアカウント(ドライブ、GAS利用)
- GASのスクリプト
- 写真置き場
LINE Developers Consoleアカウント
プロバイダやチャネルの作成については多数記事がありますので、そちらをご覧ください。
ここで使っているのは、Messaging APIチャネルです。
チャネルアクセストークンを発行し控えておきます
グループトークについて
こちらに記載がありますが、グループトークルームにBotを追加するとグループIDを取得できるので、それを元にGASからグループトークルームに対して通知を送ることができます。
グループIDを取得
"source": {
"type": "group",
"groupId": "Ca56f94637c...",
"userId": "U4af4980629..."
}
groupId
が渡されるのでこれを取得しておきます。
グループではなくとりあえず自身に通知して試してみたいときは、「チャネル基本設定」にあるユーザーIDを指定すれば試すことができます。
Googleドライブフォルダ共有
画像が入っているフォルダを共有します。共有しないと、LINEから画像リンクを参照できないため必要となります。「リンクを知っている人全員」「閲覧者」にします。
以下URLの最後の部分(folders/以降)が、一意に識別されるIDとなりますので控えておきます。
ChatGPT
ChatGPTで画像を入力できるようになったので、APIでも同様のことを試してみたく。
画像から得られる教訓を聞いてみた。
以下を参考に叩こうとしたが権限がないと!
早速叩いてみたところ、以下のエラーがでました。
なにやらGPT4を叩く権限がないとのこと・・・・
形としては、modelに gpt-4-vision-preview
を指定し、 content に指示と画像URLをセットします。今回、画像URLはGoogleドライブ上の画像にします。
色々調べているとどうもAPI利用料で1ドル?以上の請求が発生していないと使えないようです。
以下に記載がありますが、1ドルの支払いがないとGPT4が使えないということで、知らなかった。
また、アカウントを見ると CURRRENT TIER が Free となっています。
とりあえず5ドル分のクレジットを購入してみました。世の中やはりお金なのでしょうか。
やはりお金のようです。購入後 Tier1 になってました。
ということで、これで新たにAPIキーを作成して実行したところ、無事に叩けました!
Googleドライブ上の画像ファイル名、画像、渾身の一激が通知されます。
APIキーは Tier1 になってから再度作成しないとだめかもしれません。
サンプル
以下、Googleドライブの指定のフォルダ配下にある画像をランダムに1枚選び、それをグループトークルームに通知するサンプルです。ほとんどChatGPTに書いてもらいましたが。GASのトリガーで1日1回などの設定でsendRandomImageToLine
を叩けば画像と共に通知されます。
また、スクリプトプロパティに、以下を設定してからデプロイしてください。
プロパティ | 値 |
---|---|
DRIVE_DIR_ID | Googleドライブの一意のID |
LINE_ACCESS_TOKEN | MessagingAPIで発行したアクセストークン |
OPENAI_KEY | OPEN AIのキー |
TARGET_GROUP_ID | 通知先のトークグループID |
クリック
const OPENAI_KEY =
PropertiesService.getScriptProperties().getProperty("OPENAI_KEY");
const LINE_ACCESS_TOKEN =
PropertiesService.getScriptProperties().getProperty("LINE_ACCESS_TOKEN");
function callChatGptChatCompletionsApi(imageUrl) {
const apiEndpoint = "https://api.openai.com/v1/chat/completions";
const payload = {
model: "gpt-4-vision-preview",
messages: [
{
role: "user",
content: [
{
type: "text",
text: `この画像から得られる教訓を大喜利風に解説して。落語家のように400文字以内で大喜利だけを返して`,
},
{
type: "image_url",
image_url: {
url: imageUrl,
},
},
],
},
],
max_tokens: 500,
};
const headers = {
Authorization: `Bearer ${OPENAI_KEY}`,
"Content-Type": "application/json",
};
const options = {
method: "post",
headers: headers,
payload: JSON.stringify(payload),
muteHttpExceptions: true,
};
const response = UrlFetchApp.fetch(apiEndpoint, options);
return JSON.parse(response.getContentText());
}
function sendRandomImageToLine() {
const folderId =
PropertiesService.getScriptProperties().getProperty("DRIVE_DIR_ID");
const allFiles = getAllFilesInFolder(folderId);
if (allFiles.length === 0) return;
const randomFile = allFiles[Math.floor(Math.random() * allFiles.length)];
const imageUrl =
"https://drive.google.com/uc?export=view&id=" + randomFile.getId();
const fileName = randomFile.getName();
const chatGptResponse = callChatGptChatCompletionsApi(imageUrl);
var botResponse = chatGptResponse.choices[0].message.content.trim();
sendToLine(fileName, imageUrl, botResponse);
}
function getAllFilesInFolder(folderId) {
const rootFolder = DriveApp.getFolderById(folderId);
const allJpegFiles = [];
const processFolder = function (folder) {
var files = folder.getFilesByType(MimeType.JPEG);
while (files.hasNext()) {
var file = files.next();
allJpegFiles.push(file);
}
const subFolders = folder.getFolders();
while (subFolders.hasNext()) {
const subFolder = subFolders.next();
processFolder(subFolder);
}
};
processFolder(rootFolder);
return allJpegFiles;
}
function sendToLine(fileName, imageUrl, message) {
const groupId =
PropertiesService.getScriptProperties().getProperty("TARGET_GROUP_ID");
const headers = {
Authorization: `Bearer ${LINE_ACCESS_TOKEN}`,
"Content-Type": "application/json",
};
const payload = {
to: groupId,
messages: [
{
type: "text",
text: fileName,
},
{
type: "image",
originalContentUrl: imageUrl,
previewImageUrl: imageUrl,
},
{
type: "text",
text: message,
},
],
};
const options = {
method: "post",
headers: headers,
payload: JSON.stringify(payload),
};
UrlFetchApp.fetch("https://api.line.me/v2/bot/message/push", options);
}