海外のニュースレターを、自動要約・翻訳してDiscordに飛ばす
せっかく海外のニュースレター(AI、知的生産管理)を購読したが、英語のため読む気が失せた...
という経験はないだろうか?
が、そもそも無料でやりたい...!月に20$も払えない
そんな迷える子羊に、このノウハウを送ります。
アーキテクチャーは以下の通り。
ニュースレター -> Gmail -> GASで自動購読 -> GASでOpenAI APIで翻訳/要約 -> Discord へ送信
Discordの設定
自分専用のDiscordサーバーがない方は、こちらの記事を参照
ボットから情報を受け取りたいチャネル(news-letterチャネル)を右クリックし、チャネルの編集(Edit channel)を行う
連携サービス(integration) -> create webhook
「新しいウェブフック」を選択し、作成されたWebhookのURLをコピー(あとで使うので、どこかへ保存)
GASの設定
Google Driveの画面へ行く
任意のフォルダを作成(例えば、scriptsフォルダ)し、右クリック -> その他 -> Google Apps Scriptを押す
以下の画面が開くので、コードを書いていく。
無題のプロジェクトというプロジェクト名を、「海外の新着ニュースレターを翻訳/要約して、Discordへ」などに変更する
GASとDiscordを連結する
まずは、接続を確認
jsconst WEBHOOK_URL = "https://xxxxxxxxx" //ご自身のウェブフックURLに置き換えてください
function myFunction() {
const payload =
{
"content": "Hello, World!"
};
const options =
{
"method": "post",
"payload": payload
};
UrlFetchApp.fetch(WEBHOOK_URL, options);
}
command Sで保存して、コードを実行
権限を確認 -> gmailアカウントクリック -> 詳細 -> 安全ではないページに移動をクリック -> 許可
GASの実行ログとともに、Discordへも Hello World
が届いていることが確認される。
OpenAI KeyをGASへ設定
続いて、OpenAIのAPI keyを設定する。
API keyはこちらのリンクから取得する。
サイドバーのプロジェクトの設定をクリック
スクロールするとスクリプトプロパティの項目が表示されるので、「スクリプトプロパティを追加」を選択。
プロパティに「OPENAI_API_KEY」と入れて、値に事前に取得したChatGPTのAPIキーをコピペし、「スクリプトプロパティを保存」をクリック。
Gmailで条件に合ったメールを、GASへ転送する
コードはこちら
js// OpenAI の API keys (https://platform.openai.com/account/api-keys)
// スクリプトプロパティにAPIキーを保存するには、スクリプトエディタで「ファイル」>「プロジェクトのプロパティ」>「スクリプトプロパティ」を選択し、OPENAI_API_KEYとしてAPIキーを追加してください。
const OPENAI_API_KEY =
PropertiesService.getScriptProperties().getProperty("OPENAI_API_KEY")
const OPENAI_API_ENDPOINT = "https://api.openai.com/v1/chat/completions"
const OPENAI_API_MODEL = "gpt-3.5-turbo-0125"
const PROMPT_PREFIX = "あなたは情報教育、テクノロジーに詳しい教師です。\n以下のニュースレターを、タイトルと要約の2点をそれぞれ改行で分けて、日本語で説明してください。\n要点は3-5個程度、箇条書きでお願いします。\n## 出力形式 \n送信者: {送信者}\nタイトル: {タイトル}\n要約: {要約}\n日本語でお願いしますね。";
const WEBHOOK_URL = "" // 上でコピーしたWebhook urlを貼り付けてください。
// 検索クエリ(検索条件)を入れる
// ここは、各自皆様の都合で、書き換えてください。
const GMAIL_QUERY =
"is:unread from:Nick OR from:Tiago OR from:Ben's OR from:TLDR"
function searchGmailMessages(query) {
const threads = GmailApp.search(query)
Logger.log(`スレッド件数:${threads.length}`)
if (threads.length === 0) {
return []
}
return threads.map((thread) => {
const messages = thread.getMessages()
return messages[0]
})
}
function sendToDiscord(output) {
const payload = {
content: output,
}
const options = {
method: "post",
payload: payload,
}
UrlFetchApp.fetch(WEBHOOK_URL, options)
}
function callChatGPT(input) {
const messages = [
{
role: "user",
content: PROMPT_PREFIX + "\n" + input,
},
];
const options = {
"method": "post",
"headers": {
"Authorization": `Bearer ${OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
"payload": JSON.stringify({
model: OPENAI_API_MODEL,
messages,
}),
};
return JSON.parse(UrlFetchApp.fetch(OPENAI_API_ENDPOINT, options).getContentText());
}
function main() {
if (!OPENAI_API_KEY) {
console.log("ERROR: OPEN_API_KEY を指定してください")
return
}
const messages = searchGmailMessages(GMAIL_QUERY)
if (!messages.length) return
let output = "新着ニュースレターのお知らせ\n\n"
for (const message of messages) {
const sender = message.getFrom()
const title = message.getSubject()
// この入力文字エラーが解決できず、やむを得ず、replaceメソッドを使っている
// 4500文字以上を削除だと正常動作だが、5000文字以上にするとAPIエラーが返るため、4500文字としている。
// https://twitter.com/0317_hiroya/status/1783520153457906155
const content = message.getPlainBody().replace(/[\n\r]/g, '').replace(/-+|\*+/g, '').replace(/[\s\S]{4500,}/g, '');
const input =
"\n" +
"sender:" +
sender +
"title: " +
title +
"\n" +
"content: " +
content
const res = callChatGPT(input)
console.log(content)
console.log(`ChatGPTからのレスポンス: ${JSON.stringify(res, null, 2)}`)
const paragraphs = res.choices.map((c) => c.message.content.trim())
output += `${paragraphs.join("\n")}\n\n\n`
// 転送が完了したら既読にする。
message.markRead()
}
output = output.trim()
console.log(`最終結果: ${output}`)
sendToDiscord(output)
}
WEBHOOK_URLに、上で設定した値を貼り付ける
正常動作するか確認するために、エディタに戻り、main関数を実行する
うまくいったら、以下のようになる。
定期実行する
毎日決まったタイミングで処理を行うために、設定を行う。
左のサイドバーのトリガーを選択
右下のトリガーの追加から、以下の画面を設定する。(一例)
これで、毎朝午前11時-12時に、ニュースレターの翻訳/要約が届くようになった。
最後に、エディターからデプロイ -> 新しいデプロイ -> 歯車をクリック -> ウェブアプリを選択 -> デプロイで完成です。
お疲れ様でした!
備考
GASのOpenAIのAPIリクエストで、原因不明のエラーが起こった
入力文字の何らかが悪さをしているとわかったが、正規表現で様々な文字を削除しても効果がなかった。
4500文字以上の文字を全部削除にしたら問題なく動作した
5000文字以上の文字を全部削除にしたら、エラーが起きた。
入力長の関係だろうか...?
コードは汚なくなっており、今後原因が分かり次第、修正を加える
関連
参考資料