Skip to content

Instantly share code, notes, and snippets.

@tsukumonasu
Last active March 14, 2024 02:53
Show Gist options
  • Save tsukumonasu/300981ae5836d271304dff288cdd224f to your computer and use it in GitHub Desktop.
Save tsukumonasu/300981ae5836d271304dff288cdd224f to your computer and use it in GitHub Desktop.
LWTT 2024/03 名古屋のLINEWORKS ChatGPTBot作成ハンズオンの資料です。

事前準備など

前提条件

Googleアカウント

  • フリー版でもGWSでも良いですが、Google Apps ScriptをWebアプリとして公開するので、Googleアカウントを作っておく。

LINEWORKSアカウント

OpenAI アカウント

LINEWORKS&OpenAI側設定

LINEWORKS Developer Console アプリの登録

  1. 以下のURLにアクセスし、「アプリの新規追加」をクリックする。 https://dev.worksmobile.com/jp/console/openapi/v2/app/list/view image
  2. アプリの名前「LWTTHandson202403」を入力し、「同意して利用する」をクリックする。 image
  3. 「Service Account」の「発行」をクリックする(「Service Account」が表示されていなければ一旦戻ってもう一度開く。)。
  4. 「Private Key」の「発行/再発行」をクリックする(keyファイルがダウンロードされる)。
  5. 「OAuth Scopes」の「管理」をクリックする。
  6. 「bot」のみチェックし、「保存」をクリックする。 image
  7. 「Client ID」、「Client Secret」、「Service Account」はダウンロードしたkeyファイルの中身をテキストエディタにコピーしておく。

LINEWORKS Developer Console Botの登録

  1. 以下のURLにアクセスし、「登録」をクリックする。 https://dev.worksmobile.com/jp/console/bot/view image
  2. 「Bot名」に「GPTBot」、「説明」に「LWTTHandson202403」、「主担当」に自分を設定し、「保存」をクリックする。 image
  3. 「Bot ID」はあとで使うため、テキストエディタにコピーしておく。

※Callback URLはGASを作成後に設定します。

LINEWORKS 管理コンソールBot追加

  1. 以下のURLにアクセスし、「Bot追加」をクリックする。 https://admin.worksmobile.com/service/bot image
  2. 登録した「GPTBot」を選択し、「追加」をクリックする。 image
  3. 追加した「GPTBot」をクリックし、「修正」をクリックする。 image
  4. 「メンバー指定」で自分のみを設定、公開設定をオンにして、「保存」をクリックする。 image
  5. LINEWORKSのサービス通知に以下のようなトークが送信される。 image

OpenAI APIキー発行

  1. 以下URLにアクセスし、「Create new secret key」をクリックする。 https://platform.openai.com/api-keys
  2. Nameに「LWTTHandson202403」を入力し、「Create secret key」をクリックする。 image
  3. あとで使うので表示されたテキストをテキストエディタにコピーしておく。

以上

Google Apps Script WEBアプリ作成

Google 側

  1. Google SpreadSheetのコンソールを開いて、右下の「+」をクリックする。 image
  2. SpreadSheetを修正する。
  • 無題のスプレッドシートを「LWTTHandsons202403」に修正する。
  • シート名を「シート1」から「log」に修正する。
  • ヘッダーを「日付 id 問い合わせ エラー」とする。 image
  1. メニューバーの「拡張機能」→「Apps Script」をクリックして、「Apps Script」のコンソールを開く。 image
  2. このGist内の「コード.gs」をコピーして、コード.gsの中身を消した上で、貼り付ける。 image
  3. コードの内容を書き換える。
  • タイトル
    • LWTTHandson202403
  • 中身
    • GPT_API_KEY
    • LW_CLIENT_ID
    • LW_CLIENT_SECRET
    • LW_SERVICE_ACCOUNT
    • LW_PRIVATE_KEY
    • LW_BOT_ID
    • LW_NORTIFY_USER ※自分のLINEWORKSログインID
  1. 「control」+「s」をクリックしてコードを保存する。
  2. 「デバッグ」の右の「testOpenAIAPI」を選択し、「▷実行」をクリックする。
  3. 「権限を確認」をクリックする。 image
  4. ログイン中のアカウントをクリックする。 image
  5. 「許可」をクリックする。 image
  6. 実行ログに以下のようなログが出力される。 image
  7. 「デプロイ」をクリックし、「新しいデプロイ」をクリックする。 image
  8. 「⚙」をクリックし、「ウェブアプリ」をクリックする。 image
  9. 「アクセスできるユーザー」を「全員」に変更し、「デプロイ」をクリックする。 image
  10. 「アクセスを承認」をクリックする。 image
  11. ログイン中のアカウントをクリックする。
  12. 「Allow」をクリックする。
  13. 「URL」の下の「コピー」をクリックする。

LINEWORKS Developer Console Bot修正

  1. 以下URLを開き、作成したBotをクリックする。 https://dev.worksmobile.com/jp/console/bot/view
  2. 「修正」をクリックする。
  3. 「Callback URL」を「On」を選択し、GASのWEBアプリのURLを貼り付けて、「保存」をクリックする。 image

LINEWORKS側

  1. サービス通知を開き、「Bot」を利用をクリックする。 image
  2. 「利用開始」をクリックする。 image
  3. メッセージが表示される。 image
  4. 聞きたいことを入力するとBotが答えてくれる(1分ほどかかることもあるので、気長に待つ。)
  5. SpreadSheetを確認すると、「logシート」にログが出力されていることが確認できる。 image

画像生成Bot化

  1. コードの中身を「画像生成.gs」に書き換える。
  2. コードから以下を修正して保存する。
  • DALLE_MODEL_API_KEY
  • LW_CLIENT_ID
  • LW_CLIENT_SECRET
  • LW_SERVICE_ACCOUNT
  • LW_PRIVATE_KEY
  • LW_BOT_ID
  • LW_NORTIFY_USER
  1. 「デプロイ」をクリックし、「新しいデプロイ」をクリックする。
  2. 「デプロイ」をクリックする。 image
  3. ウェブアプリのURLをコピーして、「完了」をクリックする。
  4. LINEWORKS Developer Consoleを開き、Botの「Callback URL」をコピーしたURLに変更し、「保存」をクリックする。
  5. トークにテキストを入力して送信すると画像が4枚生成されるようになる。 image

画像認識Bot化

  1. SpreadSheetに「vpromt」シートを作成し、1行目のAB列に「ユーザID プロンプト」を入力する。 image
  2. コードの中身を「画像生成.gs」に書き換える。
  3. コードから以下を修正して保存する。
  • GPT4V_MODEL_API_KEY
  • LW_CLIENT_ID
  • LW_CLIENT_SECRET
  • LW_SERVICE_ACCOUNT
  • LW_PRIVATE_KEY
  • LW_BOT_ID
  • LW_NORTIFY_USER
  1. 「デプロイ」をクリックし、「新しいデプロイ」をクリックする。
  2. 「デプロイ」をクリックする。
  3. ウェブアプリのURLをコピーして、「完了」をクリックする。
  4. LINEWORKS Developer Consoleを開き、Botの「Callback URL」をコピーしたURLに変更し、「画像」にチェックをつけて、「保存」をクリックする。 image
  5. 写真をトークに送信すると画像の解析結果が表示されるようになる。 image

以上

/**
* OpenGPT 設定
*/
// const GPT_MODEL = 'gpt-3.5-turbo';
const GPT_MODEL = 'gpt-4-turbo-preview';
const GPT_URL = "https://api.openai.com/v1/chat/completions";
const GPT_API_KEY = 'xxx'
// const GPT_MAX_TOKENS = 1024;
const GPT_MAX_TOKENS = 2000;
const GPT_TEMPERATURE = 0.1; // 大きくするほど、想像力豊かになる
/**
* LINEWORKS 設定
*/
const LW_CLIENT_ID = "xxx";
const LW_CLIENT_SECRET = "xxx";
const LW_SERVICE_ACCOUNT = "xxx@xxx";
const LW_PRIVATE_KEY = `-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----`;
const LW_BOT_ID = 0;
const LW_NORTIFY_USER = 'xxx@xxx';
function openAIAPI(prompt) {
var uri = 'https://api.openai.com/v1/chat/completions';
var headers = {
'Authorization': `Bearer ${GPT_API_KEY}`,
'Content-type': 'application/json',
};
var options = {
'muteHttpExceptions': true,
'headers': headers,
'method': 'POST',
'payload': JSON.stringify({
model: `${GPT_MODEL}`,
temperature: GPT_TEMPERATURE,
max_tokens: GPT_MAX_TOKENS,
messages: [
{ role: "user", content: prompt }
]
})
};
const response = UrlFetchApp.fetch(uri, options);
var json = JSON.parse(response.getContentText());
let generatedText = json["choices"][0]['message']['content'];
return generatedText.trim();
}
function testOpenAIAPI() {
console.log(openAIAPI(`
名古屋の良いところを教えてください。`));
}
function overWriteCell() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const lastRow = sheet.getLastRow();
var range = sheet.getRange("C3:C" + lastRow);
range.setValues(range.getValues())
sheet.getSheetValues(3, 3, lastRow, 3);
}
/**
* Lineworksからのコールバック受付
*/
function doPost(e) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('log');
const nextRow = sheet.getLastRow() + 1;
sheet.getRange(nextRow, 1).setValue(Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss'));
try {
const contents = JSON.parse(e.postData.contents); // json to dic
const userId = contents.source.userId;
sheet.getRange(nextRow, 2).setValue(userId);
const talkMessage = contents.content['text'];
sheet.getRange(nextRow, 3).setValue(talkMessage);
// sheet.getRange(nextRow, 5).setValue(e);
if (talkMessage == '利用開始') {
// sendText(userId, 'こんにちは、私はOpenAIのチャットボットです。私は自然言語処理技術を使って、人工知能による自動応答を行うことができます。私は、様々な分野での情報収集や問題解決に役立つことができます。よろしくお願いします。' + '\n※文脈を読む機能には対応していません。');
sendText(userId,`私はOpenAI社が開発したAIを利用したチャットボットです。以下のようなことに活用いただけます。
※文脈を読む機能には対応していません。
1. カスタマーサポート:
- チャットボットとして顧客からの問い合わせにリアルタイムで対応。
- サイズガイド、在庫状況、配送情報などの基本的な質問に答える。
- 返品・交換ポリシーの説明や手続きの支援。
2. パーソナライズされたショッピングアシスタント:
- 顧客の好みや購買履歴に基づいて、パーソナライズされた商品推薦を提供。
- スタイリングのアドバイスやコーディネートの提案。
3. マーケティングとコミュニケーション:
- ソーシャルメディアやメールマーケティングでのキャンペーンの作成支援。
- プロモーションや新商品の発表時に、顧客とのエンゲージメントを高めるためのコンテンツ作成。
4. トレンド分析と市場調査:
- ソーシャルメディアやレビューサイトからのデータを分析して、トレンドを把握。
- 顧客のフィードバックやレビューを分析して、商品開発や品質改善に活かす。
5. 在庫管理と需要予測:
- 過去の販売データを分析して、需要予測を行い、在庫管理を最適化。
- セールスパターンを分析して、過剰在庫や品切れを防ぐ。
6. 製品説明とコンテンツ生成:
- 商品の特徴や素材、お手入れ方法などの詳細な説明文の作成。
- SEOに最適化された製品説明でウェブサイトのトラフィックを増やす。
7. 言語翻訳と多言語サポート:
- 多言語での顧客サポートを提供し、国際的な顧客基盤を拡大。
- ウェブサイトや商品説明の翻訳を行い、異なる市場に対応。
8. 教育とトレーニング:
- 社内の新入社員やスタッフ向けの製品知識や販売技術のトレーニング資料の作成。
- よくある質問やケーススタディを使って、顧客対応スキルを向上させる。
9. フィードバックとレビューの収集:
- 顧客からのフィードバックやレビューを収集し、製品やサービスの改善点を特定。
- ポジティブなレビューをマーケティング資料として活用。
10. クリエイティブなコンテンツ生成:
- ブランドストーリーやブログ記事、ソーシャルメディア投稿のためのクリエイティブなコンテンツの生成。`);
return;
}
const openAIAPIMessage = openAIAPI(talkMessage);
sheet.getRange(nextRow, 4).setValue(openAIAPIMessage);
sendText(userId, openAIAPIMessage);
}
catch (error) {
sendText(userId, '生成に失敗しました。');
errorMessage = getErrorMessage(error);
sheet.getRange(nextRow, 4).setValue(errorMessage);
sendText(LW_NORTIFY_USER, errorMessage); // 管理者にエラーを送信
}
}
function getErrorMessage(error) {
return "[名前] " + error.name + "\n" +
"[場所] " + error.fileName + "(" + error.lineNumber + "行目)\n" +
"[メッセージ]" + error.message + "\n" +
"[StackTrace]\n" + error.stack;
}
/**
* テキスト送信
*/
function sendText(userId, talkMessage) {
if (talkMessage == '') {
return;
}
const content = {
"type": "text",
"text": talkMessage
};
const options = {
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + getToken(),
},
'payload': JSON.stringify({
"accountId": userId,
"content": content
}),
'muteHttpExceptions': true,
};
UrlFetchApp.fetch(`https://www.worksapis.com/v1.0/bots/${LW_BOT_ID}/users/${userId}/messages`, options);
}
// function testSendText() {
// sendText(nortifyUser, 'ほげ')
// }
function getJwt() {
const header = Utilities.base64Encode(JSON.stringify({ "alg": "RS256", "typ": "JWT" }), Utilities.Charset.UTF_8)
const claimSet = JSON.stringify({
"iss": LW_CLIENT_ID,
"sub": LW_SERVICE_ACCOUNT,
"iat": Math.floor(Date.now() / 1000),
"exp": Math.floor(Date.now() / 1000 + 2000)
})
const encodeText = header + "." + Utilities.base64Encode(claimSet, Utilities.Charset.UTF_8)
const signature = Utilities.computeRsaSha256Signature(encodeText, LW_PRIVATE_KEY)
return encodeText + "." + Utilities.base64Encode(signature)
}
// function testGetJwt() {
// console.log(getJwt());
// }
function getToken() {
const uri = "https://auth.worksmobile.com/oauth2/v2.0/token"
const payload = {
"assertion": getJwt(),
"grant_type": encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"),
"client_id": LW_CLIENT_ID,
"client_secret": LW_CLIENT_SECRET,
"scope": 'bot'
}
const options = {
"method": "post",
"headers": { "Content-Type": "application/x-www-form-urlencoded" },
"payload": payload
}
return JSON.parse(UrlFetchApp.fetch(uri, options)).access_token;
}
// function testGetToken() {
// console.log(getToken());
// }
/**
* OpenGPT 設定
*/
const DALLE_MODEL = 'dall-e-3';
const DALLE_MODEL_URL = "https://api.openai.com/v1/images/generations";
const DALLE_MODEL_API_KEY = 'xxx'
const DALLE_MODEL_SIZE = '1024x1024';
const DALLE_MODEL_NUMBER = 1; // 1しか指定できない
/**
* LINEWORKS 設定
*/
const LW_CLIENT_ID = "xxx";
const LW_CLIENT_SECRET = "xxx";
const LW_SERVICE_ACCOUNT = "xxx@xxx";
const LW_PRIVATE_KEY = `-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----`;
const LW_BOT_ID = 0;
const LW_NORTIFY_USER = 'xxx@xxx';
function callImagesGeneration(prompt) {
var options = {
"method": 'POST',
"headers": {
'Authorization': `Bearer ${DALLE_MODEL_API_KEY}`,
'Content-type': 'application/json',
},
"payload": JSON.stringify({
"model": DALLE_MODEL,
"prompt": prompt,
"n": DALLE_MODEL_NUMBER,
"size": DALLE_MODEL_SIZE,
"response_format": "url"
}),
muteHttpExceptions: true
}
var response = UrlFetchApp.fetch(DALLE_MODEL_URL, options);
var responseJson = response.getContentText();
var responseData = JSON.parse(responseJson);
// Logger.log(responseData);
return responseData['data']
}
// function testCallImagesGeneration() {
// var result = callImagesGeneration('NAIKIと文字の入ったロゴを作って。斧持ったモヒカンの侍のイラストをつけて')
// Logger.log(result);
// }
function overWriteCell() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const lastRow = sheet.getLastRow();
var range = sheet.getRange("C3:C" + lastRow);
range.setValues(range.getValues())
sheet.getSheetValues(3, 3, lastRow, 3);
}
/**
* Lineworksからのコールバック受付
*/
function doPost(e) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('log');
const nextRow = sheet.getLastRow() + 1;
sheet.getRange(nextRow, 1).setValue(Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss'));
try {
const contents = JSON.parse(e.postData.contents); // json to dic
const userId = contents.source.userId;
sheet.getRange(nextRow, 2).setValue(userId);
const talkMessage = contents.content['text'];
sheet.getRange(nextRow, 3).setValue(talkMessage);
// sheet.getRange(nextRow, 5).setValue(e);
if (talkMessage == '利用開始') {
sendText(userId, `こんにちは、私は画像生成AIのDALL·Eを利用してイメージを生成するBotです。
※1枚$0.04かかりますので、あまり呼び出し過ぎないようにしてください。
※DALL·E3で生成された画像につきましては公式サイトにて商用利用可と記載がございますが、業務で利用する場合、上長や関連部署に確認の上、ご利用ください。
※オリジナルサイズの画像は2時間だけしかダウンロードできないので、保存しておきたい画像は早めにダウンロードしてください。`);
return;
}
for (let i = 0; i < 4; i++) {
const openAIAPIMessage = callImagesGeneration(talkMessage);
sheet.getRange(nextRow, 4).setValue(openAIAPIMessage);
sendImages(userId, openAIAPIMessage);
}
}
catch (error) {
sendText(userId, '生成に失敗しました。');
errorMessage = getErrorMessage(error);
sheet.getRange(nextRow, 4).setValue(errorMessage);
sendText(LW_NORTIFY_USER, errorMessage); // 管理者にエラーを送信
}
}
function getErrorMessage(error) {
return "[名前] " + error.name + "\n" +
"[場所] " + error.fileName + "(" + error.lineNumber + "行目)\n" +
"[メッセージ]" + error.message + "\n" +
"[StackTrace]\n" + error.stack;
}
/**
* テキスト送信
*/
function sendImages(userId, dataArray) {
for (let data in dataArray) {
//取得したオブジェクトのキーをログ出力
sendText(userId, dataArray[data]['url'])
sendImage(userId, dataArray[data]['url'])
}
}
// function testSendImages() {
// var results = callImagesGeneration('NAIKIと文字の入ったロゴを作って。斧持ったモヒカンの侍のイラストをつけて');
// sendImages(LW_NORTIFY_USER, results);
// }
function sendImage(userId, url) {
const content = {
"type": "image",
"previewImageUrl": url,
"originalContentUrl": url
};
const options = {
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + getToken(),
},
'payload': JSON.stringify({
"accountId": userId,
"content": content
}),
'muteHttpExceptions': true,
};
UrlFetchApp.fetch(`https://www.worksapis.com/v1.0/bots/${LW_BOT_ID}/users/${userId}/messages`, options);
}
/**
* テキスト送信
*/
function sendText(userId, talkMessage) {
if (talkMessage == '') {
return;
}
const content = {
"type": "text",
"text": talkMessage
};
const options = {
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + getToken(),
},
'payload': JSON.stringify({
"accountId": userId,
"content": content
}),
'muteHttpExceptions': true,
};
UrlFetchApp.fetch(`https://www.worksapis.com/v1.0/bots/${LW_BOT_ID}/users/${userId}/messages`, options);
}
function getJwt() {
const header = Utilities.base64Encode(JSON.stringify({ "alg": "RS256", "typ": "JWT" }), Utilities.Charset.UTF_8)
const claimSet = JSON.stringify({
"iss": LW_CLIENT_ID,
"sub": LW_SERVICE_ACCOUNT,
"iat": Math.floor(Date.now() / 1000),
"exp": Math.floor(Date.now() / 1000 + 2000)
})
const encodeText = header + "." + Utilities.base64Encode(claimSet, Utilities.Charset.UTF_8)
const signature = Utilities.computeRsaSha256Signature(encodeText, LW_PRIVATE_KEY)
return encodeText + "." + Utilities.base64Encode(signature)
}
// function testGetJwt() {
// console.log(getJwt());
// }
function getToken() {
const uri = "https://auth.worksmobile.com/oauth2/v2.0/token"
const payload = {
"assertion": getJwt(),
"grant_type": encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"),
"client_id": LW_CLIENT_ID,
"client_secret": LW_CLIENT_SECRET,
"scope": 'bot'
}
const options = {
"method": "post",
"headers": { "Content-Type": "application/x-www-form-urlencoded" },
"payload": payload
}
return JSON.parse(UrlFetchApp.fetch(uri, options)).access_token;
}
// function testGetToken() {
// console.log(getToken());
// }
/**
* OpenGPT 設定
*/
const GPT4V_MODEL = 'gpt-4-vision-preview';
const GPT4V_MODEL_URL = "https://api.openai.com/v1/chat/completions";
const GPT4V_MODEL_API_KEY = 'xxx'
const GPT4V_PROMPT = 'この画像になにが書いてあるか教えて';
/**
* LINEWORKS 設定
*/
const LW_CLIENT_ID = "xxx";
const LW_CLIENT_SECRET = "xxx";
const LW_SERVICE_ACCOUNT = "xxx@xxx";
const LW_PRIVATE_KEY = `-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----`;
const LW_BOT_ID = 0;
const LW_NORTIFY_USER = 'xxx@xxx';
function overWriteCell() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const lastRow = sheet.getLastRow();
var range = sheet.getRange("C3:C" + lastRow);
range.setValues(range.getValues())
sheet.getSheetValues(3, 3, lastRow, 3);
}
/**
* Lineworksからのコールバック受付
*/
function doPost(e) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('log');
const nextRow = sheet.getLastRow() + 1;
sheet.getRange(nextRow, 1).setValue(Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss'));
try {
const contents = JSON.parse(e.postData.contents); // json to dic
const userId = contents.source.userId;
sheet.getRange(nextRow, 2).setValue(userId);
sheet.getRange(nextRow, 3).setValue(contents);
if ('text' == contents.content.type) {
// テキストの場合
const talkMessage = contents.content['text'];
if (talkMessage == '利用開始') {
sendText(userId, `こんにちは、私はGPT-4Visionを使って画像を解析するBotです。
メッセージを打ち込むとプロンプトが置き換わります。
最初は「${GPT4V_PROMPT}」が設定されています。`);
setVisionPrompt(userId, GPT4V_PROMPT);
return;
}
setVisionPrompt(userId, talkMessage);
const prompt = getVisionPrompt(userId);
sendText(userId, `「${prompt}」をプロンプトに設定しました`);
sheet.getRange(nextRow, 3).setValue(talkMessage);
return;
}
if ('image' == contents.content.type) {
const imageVision = getImageVison(contents.content.fileId, getVisionPrompt(userId));
sendText(userId, imageVision);
sheet.getRange(nextRow, 4).setValue(imageVision);
}
}
catch (error) {
sendText(userId, '生成に失敗しました。');
errorMessage = getErrorMessage(error);
sheet.getRange(nextRow, 4).setValue(errorMessage);
sendText(LW_NORTIFY_USER, errorMessage); // 管理者にエラーを送信
}
}
/**
* ユーザごとのプロンプトを設定する
*/
function setVisionPrompt(userId, visionPrompt) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('vpromt');
var userColumn = sheet.getRange("A:A").getValues();
var rowIndex = userColumn.findIndex(row => row[0] == userId) + 1;
if (rowIndex != 0) {
// ユーザIDが見つかった場合はその行のB列を上書き
sheet.getRange(rowIndex, 2).setValue(visionPrompt);
return;
}
sheet.appendRow([userId, visionPrompt]);
}
/**
* ユーザごとのプロンプトを取得する
*/
function getVisionPrompt(userId) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('vpromt');
var userColumn = sheet.getRange("A:A").getValues();
var rowIndex = userColumn.findIndex(row => row[0] == userId) + 1;
if (rowIndex == 0) {
return GPT4V_PROMPT;
}
// 行インデックスのB列の値を取得
var visionPrompt = sheet.getRange(rowIndex, 2).getValue();
return visionPrompt;
}
function getImageVison(fileId, prompt) {
let image = UrlFetchApp.fetch(`https://www.worksapis.com/v1.0/bots/${LW_BOT_ID}/attachments/${fileId}`, {
"headers": {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + getToken(),
},
"method": "get"
});
let imageData = image.getContent();
let base64Image = Utilities.base64Encode(imageData);
return callOpenAiApi(base64Image, prompt);
}
function callOpenAiApi(base64Image, prompt) {
let options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + GPT4V_MODEL_API_KEY,
},
payload: JSON.stringify({
'model': GPT4V_MODEL,
'messages': [
{
'role': 'user',
'content': [
{
'type': 'text',
'text': prompt
},
{
'type': 'image_url',
'image_url': {
'url': "data:image/jpeg;base64," + base64Image
}
}
]
}
],
'max_tokens': 2000 // 返信の最大トークン数
})
};
let response = UrlFetchApp.fetch(GPT4V_MODEL_URL, options);
let responseMessage = JSON.parse(response).choices[0].message.content
return responseMessage;
}
function getErrorMessage(error) {
return "[名前] " + error.name + "\n" +
"[場所] " + error.fileName + "(" + error.lineNumber + "行目)\n" +
"[メッセージ]" + error.message + "\n" +
"[StackTrace]\n" + error.stack;
}
/**
* テキスト送信
*/
function sendText(userId, talkMessage) {
if (talkMessage == '') {
return;
}
const content = {
"type": "text",
"text": talkMessage
};
const options = {
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + getToken(),
},
'payload': JSON.stringify({
"accountId": userId,
"content": content
}),
'muteHttpExceptions': true,
};
UrlFetchApp.fetch(`https://www.worksapis.com/v1.0/bots/${LW_BOT_ID}/users/${userId}/messages`, options);
}
function getJwt() {
const header = Utilities.base64Encode(JSON.stringify({ "alg": "RS256", "typ": "JWT" }), Utilities.Charset.UTF_8)
const claimSet = JSON.stringify({
"iss": LW_CLIENT_ID,
"sub": LW_SERVICE_ACCOUNT,
"iat": Math.floor(Date.now() / 1000),
"exp": Math.floor(Date.now() / 1000 + 2000)
})
const encodeText = header + "." + Utilities.base64Encode(claimSet, Utilities.Charset.UTF_8)
const signature = Utilities.computeRsaSha256Signature(encodeText, LW_PRIVATE_KEY)
return encodeText + "." + Utilities.base64Encode(signature)
}
// function testGetJwt() {
// console.log(getJwt());
// }
function getToken() {
const uri = "https://auth.worksmobile.com/oauth2/v2.0/token"
const payload = {
"assertion": getJwt(),
"grant_type": encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"),
"client_id": LW_CLIENT_ID,
"client_secret": LW_CLIENT_SECRET,
"scope": 'bot'
}
const options = {
"method": "post",
"headers": { "Content-Type": "application/x-www-form-urlencoded" },
"payload": payload
}
return JSON.parse(UrlFetchApp.fetch(uri, options)).access_token;
}
// function testGetToken() {
// console.log(getToken());
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment