
【OpenAI API&GAS】メンションするとテキスト翻訳してくれるLINE Bot作った
今回はGoogle App Script(GAS)とOpenAI API(gpt-4o-mini)を使って、メンションするとメッセージを翻訳してくれるLINE Botを作成したので、その備忘録です。
背景:住んでいるシェアハウスがだんだんグローバルに
僕は都内で50人近くが住むシェアハウス(ソーシャルアパートメント)で暮らしています。住人同士の交流も盛んで、よくグループLINEで呼びかけてみんなで遊ぶ、みたいな機会がよくあります。そんなシェアハウスにですが、最近外国の方が入居してくるようになってきました。オーストラリアとか台湾とか、フランスとか…
シェアハウスのグループLINEのやり取りはもちろん日本語なので、外国から来た方がシェアハウスでの交流に参加しづらい状況でした。LINEには公式から提供されている通訳機能がありますが、投稿されたメッセージ全てをBotが翻訳する仕様になっているため、50名程度が参加しているLINEグループで利用するにはToo Muchでした。
シェアメイトがそんなことを議論しているのを聞いていて、GPT使えば簡単に柔軟に翻訳Botが作れそうだなと思い、今回の実装に至りました。
GPTで翻訳してくれるLINE Botを作成
今回作成したのは、LINE Messaging APIからイベント情報を取得し、OpenAI APIにメッセージを渡して翻訳結果を取得、Botから返信するGASプログラムです。

今回のGASを動かすのに必要なのは、以下の三点です。
LINEチャネルアクセストークン
OpenAI APIキー
BotのユーザーID
1. LINEチャネルアクセストークンの取得
LINEチャンネルアクセストークンを取得するには、LINE公式アカウントを作成し、Messaging APIの利用を有効化します。
チャンネルのMessaging APIの利用を有効化した後、LINE Developerコンソール上で当チャンネルのMessaging API設定タブを開き、最下部までスクロールするとチャネルアクセストークンを発行・取得できます。
このトークンを使うことで、LINEから当チャネルに関わるイベントの情報を取得できるようになります。

紙幅の都合上、具体的なステップバイステップの説明は割愛しますが、LINE Messaging API利用までの流れは以下の公式ドキュメントが参考になるかと思います。
LINE Developerコンソールを初めて使う人は、先にプロバイダーを作成する必要があります。
2. OpenAI APIキーの取得
次に、OpenAI APIを利用するためのAPIキーを発行します。手順はだいたいこんなかんじ。
サイドバーの「API Keys」をクリック
「Create new secret key」をクリック
Name欄にキーの名前を入力する
「ショートカットで翻訳」とかでおk
Permissionsは「All」のままでいいと思います
「Create secret key」をクリック
シークレットキーが表示されるのでコピーして大事に保管
以前に書いた別の記事でもOpenAI APIを使っているので、こちらも見てみてください〜
3. BotのユーザーIDの取得
BotのユーザーIDですが、これはグループ内で自身がメンションされているかどうかを判別するために必要です。これは以下のコマンドラインをターミナルなどで実行して取得するのが良いかと思います(channel access tokenの部分は前項で取得した値に置き換えます)。
curl -X GET -H 'Authorization: Bearer {channel access token}' https://api.line.me/v2/bot/info
コンソールからも「あなたのユーザーID」というそれっぽい項目が確認できるのですが、上述のコマンドラインで取得できる値とここに表示されている値は異なります。コマンドラインで取得した方を使います(紛らわしい…)。この辺の仕様はちょっとよくわかっていないです。

4. GASプロジェクトを作成
GASを動かすための材料が揃ったら、GASのプロジェクトを作成します。まず、スクリプトプロパティに以下の項目を追加し、ここまでで取得した情報を格納します。
LINE_ACCESS_TOKEN:1で取得した文字列
OPENAI_APIKEY:2で取得した文字列
BOT_USER_ID:3で取得した文字列
次に、スクリプトエディタに以下のコードを貼り付けます。
function doPost(e) {
const props = PropertiesService.getScriptProperties()
const event = JSON.parse(e.postData.contents).events[0]
const botUserId = props.getProperty(`BOT_USER_ID`)
let userMessage = event.message.text
const mentions = event.message.mention ? event.message.mention.mentionees : []
const isMentioned = mentions.some(m => m.userId === botUserId);
if(!isMentioned){
return;
}
const prompt = `ユーザーから渡されるメッセージの言語を抽出して、日本語なら英語へ、英語なら日本語へ翻訳して。冒頭の説明や結びは省略して翻訳文のみを出力して。また、出力に含まれる@hogeのようなメンション部分は除外して。`;
const requestOptions = {
"method": "post",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer "+ props.getProperty('OPENAI_APIKEY')
},
"payload": JSON.stringify({
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": prompt},
{"role": "user", "content": userMessage}
]
})
}
const response = UrlFetchApp.fetch("https://api.openai.com/v1/chat/completions", requestOptions)
const responseText = response.getContentText();
const json = JSON.parse(responseText);
const text = json['choices'][0]['message']['content'].trim();
UrlFetchApp.fetch('https://api.line.me/v2/bot/message/reply', {
'headers': {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ' + props.getProperty('LINE_ACCESS_TOKEN'),
},
'method': 'post',
'payload': JSON.stringify({
'replyToken': event.replyToken,
'messages': [{
'type': 'text',
'text': text,
}]
})
})
}
5. GASプロジェクトをデプロイ
以下の設定でプロジェクトをデプロイします。
デプロイの種類:ウェブアプリ
次のユーザーとして実行:自分
アクセスできるユーザー:全員

デプロイが完了すると、以下のようなURLを取得できます。
https://script.google.com/macros/s/AKfycb....xahg/exec
6. LINEでWebhook URL設定
最後に、GAS側で取得したURLをLINEコンソール側(Messaging API設定)でイベント情報の送信先として設定します。

「検証」ボタンを押して「成功」というプロンプトが表示されたら成功です。ここで302、タイムアウト等で検証に失敗した場合は、
スクリプトプロパティの情報が正しく設定されているかどうか
プロジェクトのデプロイ設定が正しいかどうか
をチェックしてみてください。
翻訳botをテストしてみる
作成したBotを専用のグループに招待して動きをテストしてみましょう。Botをメンションしてメッセージの翻訳文が返ってきたら成功です。
以下のようなシステムプロンプトを組み込んでいるので、日本語は英語に、英語は日本語に翻訳してくれます。
ユーザーから渡されるメッセージの言語を抽出して、日本語なら英語へ、英語なら日本語へ翻訳して。冒頭の説明や結びは省略して翻訳文のみを出力して。また、出力に含まれる@hogeのようなメンション部分は除外して。

生成AIで手軽に柔軟な実装が可能に
LINEの公式の翻訳機能がチャット内の全てのメッセージを翻訳する一方、今回のBotの場合はメッセージが翻訳されるかどうかは投稿主の親切心に委ねられます。そういう場合、個人的には何もしないでいるとしばらく経たないうちに使われなくなると思っているので、その点で限界を感じています。
とはいえ、今回のような手軽な実装によって問題を解決するための新たな選択肢を一つ提示できるのはとても面白いなと思っています。
生成AIを使えるようになったことで、翻訳のような特定のテキスト処理を自然言語による命令で簡単に実装できるようになっています。今後も同じ要領で自分や周りの人間が便利に使えるようなツールを作っていけたらなーと考えている次第です。