-
Notifications
You must be signed in to change notification settings - Fork 0
/
geminibot.gs
157 lines (145 loc) · 5.09 KB
/
geminibot.gs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// 環境変数からAPIキーとトークン、フォルダIDを取得
const GEMINI_API_KEY = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
const LINE_CHANNEL_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty('LINE_CHANNEL_ACCESS_TOKEN');
const OUT_DIR_ID = PropertiesService.getScriptProperties().getProperty('OUT_DIR_ID');
const REPLY_URL = 'https://api.line.me/v2/bot/message/reply';
const sCache = CacheService.getScriptCache();
/**
* LINEのトークでメッセージが送信された際に起動するメソッド
*/
function doPost(e) {
const eventData = JSON.parse(e.postData.contents).events[0],
repToken = eventData.replyToken,
msgType = eventData.message.type;
if (msgType == 'text') {
let uText = eventData.message.text, gemini;
if (!sCache.get('image')) {
gemini = getGeminiProAnswerTxt(uText);
} else {
gemini = getGeminiProVisionAnswerTxt(uText, sCache.get('image'));
DriveApp.getFileById(sCache.get('image')).setTrashed(true);
sCache.remove('image');
}
replyTxt(repToken, gemini);
sCache.put('user', uText.slice(0, 10000));
sCache.put('model', gemini.slice(0, 10000));
} else if (msgType == 'image') {
let imageId = getImageId4Create(eventData);
sCache.put('image', imageId);
replyTxt(repToken, '送信された画像について聞きたいことは何ですか?');
}
}
/**
* LINEのトークに送信されたメッセージをGemini Pro APIに渡して回答を得るメソッド
*/
function getGeminiProAnswerTxt(txt) {
let contentsStr = '';
if (sCache.get('user')) {
contentsStr += `{
"role": "user",
"parts": [{
"text": ${JSON.stringify(sCache.get('user'))}
}]
},{
"role": "model",
"parts": [{
"text": ${JSON.stringify(sCache.get('model'))}
}]
},`;
}
contentsStr += `{
"role": "user",
"parts": [{
"text": ${JSON.stringify(txt)}
}]
}`;
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent?key=${GEMINI_API_KEY}`,
payload = {
'contents': JSON.parse(`[${contentsStr}]`)
},
options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(payload)
};
const res = UrlFetchApp.fetch(url, options),
resJson = JSON.parse(res.getContentText());
if (resJson && resJson.candidates && resJson.candidates.length > 0) {
return resJson.candidates[0].content.parts[0].text;
} else {
return '回答を取得できませんでした。';
}
}
/**
* LINEのトークで送信された画像をGoogleドライブに保存し、ファイルIDを返却するメソッド
*/
function getImageId4Create(e) {
const url = `https://api-data.line.me/v2/bot/message/${e.message.id}/content`,
options = {
'method': 'get',
'headers': {
'Authorization': `Bearer ${LINE_CHANNEL_ACCESS_TOKEN}`,
}
};
const data = UrlFetchApp.fetch(url, options),
imageData = data.getBlob().getAs('image/png').setName(Number(new Date()));
return DriveApp.getFolderById(OUT_DIR_ID).createFile(imageData).getId();
}
/**
* LINEのトークに送信されたメッセージをGemini Pro Vision APIに渡して回答を得るメソッド
*/
function getGeminiProVisionAnswerTxt(txt, imageid) {
try {
let file = DriveApp.getFileById(imageid);
let blob = file.getBlob();
let base64Data = Utilities.base64Encode(blob.getBytes());
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${GEMINI_API_KEY}`,
payload = {
'contents': [{
'parts': [{
'text': txt
},{
'inlineData': {
'mimeType': 'image/png', // MIMEタイプを正しく指定
'data': base64Data
}
}]
}]
},
options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(payload)
};
const res = UrlFetchApp.fetch(url, options),
resJson = JSON.parse(res.getContentText());
if (resJson && resJson.candidates && resJson.candidates.length > 0) {
return resJson.candidates[0].content.parts[0].text;
} else {
return '申し訳ございません。お答えできません。';
}
} catch (ex) {
return '申し訳ございません。Gemini Proの呼び出しで異常終了しました。';
}
}
/**
* LINEのトークにメッセージを返却するメソッド
*/
function replyTxt(token, txt) {
const message = {
'replyToken': token,
'messages': [{
'type': 'text',
'text': txt
}]
},
options = {
'method': 'post',
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': `Bearer ${LINE_CHANNEL_ACCESS_TOKEN}`,
},
'payload': JSON.stringify(message)
};
UrlFetchApp.fetch(REPLY_URL, options);
}