generated at
海外のニュースレターを、自動要約・翻訳してDiscordに飛ばす
せっかく海外のニュースレター(AI、知的生産管理)を購読したが、英語のため読む気が失せた...
という経験はないだろうか?

有料版のZapierを使えば、簡単にできる。
が、そもそも無料でやりたい...!月に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を連結する
まずは、接続を確認

js
const 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へ転送する
自分の場合は、 AI情報に関する有益なニュースレター知的生産管理に関する有益なニュースレターを購読し、Gmailに送っている。これらを、Gmailで絞り込み検索をするの情報を参考に、フィルター検索をかける。

コードはこちら
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文字以上の文字を全部削除にしたら、エラーが起きた。
入力長の関係だろうか...?
コードは汚なくなっており、今後原因が分かり次第、修正を加える


関連


参考資料