見出し画像

[GASで自動化] Dropboxに保存してメルマガを耳で聞く方法

みなさん、長文のメルマガってどうやって読まれてますか?
私はAndroidスマホアプリの「T2S」を使って耳で聞いてるのですが、このアプリを使うときにわざわざメールをアプリにコピペする一手間がめんどくさい!
というわけで作ったのがこの記事でご紹介する自動化方法になります。

この記事ではGmailに届いたメールの本文をDropboxフォルダにテキストファイルとして保存する方法をご紹介します。Google APP Script(GAS)からDropboxにアップロードする方法の情報があまり出てこなかったので、忘備録を兼ねて書くことにしました。

ちなみにGASからならGoogle Driveの方が断然アクセスしやすいのになぜDropboxかというと、それは私がDropbox好きだからです(パソコンからローカルファイルのように扱える点がGoogle Driveより使い勝手が良いから)。

必要なこと

  • Google App Script(GAS)のセットアップ

  • Dropboxアプリの作成

  • PostmanからDropbox APIの「リフレッシュトークン」の取得

  • GASからDropbox APIリクエストを送る

順番に詳しく説明していきます。

Google App Script(GAS)のセットアップ

まず最初にGoogle Sheetを開きます。

そして、エクステンションメニューから「Apps Script」を選択します。
すると、以下の画面になりますのでプロジェクト名を「Untitled Project」から何か適当なものに変更します。

次に、Gmailを取得する関数を書きます。

function myFunction() {
  // Gmailからメールを取得
  const threads = GmailApp.search('in:inbox newer_than:1d');
  // 取得したメールを処理
  threads.forEach(thread => {
    const messages = thread.getMessages();
    messages.forEach(message => {
      const subject = message.getSubject(); //メール題名
      const body = message.getPlainBody(); //メール本文
      const date = Utilities.formatDate(message.getDate(), "Tokyo/Japan", "yyyy-MM-dd") //メールの送信時間

    console.log(subject, body, date) //実行ログを出力
    
    });
  });
}

Gmailの取得には「GmailApp.search」関数を使います。関数についての詳しい説明は公式ドキュメントを参照してください。この関数の引数はGmailアプリ共通のクエリ文法が使えます。(例:in:inbox label:my-label is:unread)

ではここで一旦確認のために実行してみます。
右上の「Deploy(デプロイ)」ボタンを押し、「New Deployment(新規デプロイ)」を選択します。
タイプから「Web app(ウェブアプリ)」を選択し名前を適当につけて「Deploy(デプロイ)」ボタンをクリックします。

続いて「Authorize Access(アクセス許可する)」ボタンをクリックします。


画面のプロンプトに従って自分のGoogleアカウントを選択すると以下のワーニングメッセージが表示されます。
ここでは青いボタンでなく、「Advanced」オプションをクリックします。


さらに「Go to <アプリ名> (unsafe)」のオプションを選択します。


すると以下の確認画面が表示されるので「Allow(許可する)」をクリックします。


すると無事デプロイが行われ以下の画面が表示されるので、「Done(終了)」で画面を閉じます。


それでは、画面上部の「Run(実行)」ボタンをクリックしてコードを実行してみましょう。

Execution Log(実行ログ)にログが表示され、メールを取得できていることが確認できます。

Dropboxアプリの作成

Dropboxの開発者ページにログインします。
アプリの作成メニューから新しくアプリの作成を行います。

画面に従って進むと以下のページが表示されます。



今回はPostmanを使ってリフレッシュトークン(詳しくは後述)を取得していくので、Redirect URIsにはhttps://oauth.pstmn.io/v1/browser-callbackと入力します。
あとはデフォルトのままでOKです。App keyとApp secretは後で使います。

数年前からDropbox APIのアクセストークンは"short-lived"(短時間でアクセス期限切れとなるトークン)のみのサポートとなりました。詳しくはこちらのページを参照してください。

このページを見ると、

We offer a long-lived refresh_token that can be used to request a new, short-lived access token.

https://dropbox.tech/developers/migrating-app-permissions-and-access-tokens#updating-access-token-type

と書かれています。つまり、長期利用可能なリフレッシュトークンを発行し、そのリフレッシュトークンを使って短時間アクセストークンを発行する流れのようです。またtoken_access_typeをofflineオプションとすることで、ユーザーが再承認しなくても期限切れしないリフレッシュトークンを発行できるようです(参考)。

ちなみに上記アプリページの"Generate access token"では"Generate"ボタンから短時間アクセストークン(短時間アクセストークンは"sl."から始まります)を発行可能ですが、今回は以下のステップでリフレッシュトークンを使ってAPI経由でアクセストークンを自動発行するので使いません。

  1. Postmanアプリから認証リクエスト(https://www.dropbox.com/oauth2/authorize)を送ってリフレッシュトークン(長期利用可能)を取得

  2. リフレッシュトークンを使ってトークンリクエスト(https://api.dropboxapi.com/oauth2/token)を送ってアクセストークン(short-lived)を取得

  3. アクセストークンを使ってアップロードリクエストによりファイルアップロードする。

参照:https://developers.dropbox.com/ja-jp/oauth-guide

PostmanからDropbox APIの「リフレッシュトークン」の取得

Postmanの新規リクエストを開いて「認可」タブに以下のように入力します。
クライアントIDとクライアントシークレットにはそれぞれApp keyとApp secretをDropboxのアプリコンソールからコピーします。Scopeにはアプリコンソールで追加したScopeを入力します。

これで、画面下の「新しいアクセストークンを取得」をクリックするとDropboxのアカウント認証画面が表示され、リフレッシュトークンを取得できます。



注意:APIキーやトークンのセキュリティは非常に重要です。PostmanのVault機能やシークレットマネージャーを利用し管理することをお勧めします。

GASからDropbox APIリクエストを送る

ここでは以下のタスクを行います。

  1. リフレッシュトークンを使ってアクセストークンを発行する

  2. メールの文面をファイルアップロードする

まず1の関数をGASに追加します。

// Dropboxのアクセストークンを発行する関数
function getDropboxAccessToken(refresh, app_id, app_secret) {

  const options = {
    "method": "post",
    "headers": {
      "Content-Type" : "application/x-www-form-urlencoded"
    },
    "payload": {
      "refresh_token": refresh,
      "grant_type": "refresh_token" ,
      "client_id": app_id,
      "client_secret": app_secret,
    },
    "muteHttpExceptions" : true
  }
  
  const response = UrlFetchApp.fetch('https://api.dropbox.com/oauth2/token', options)
  console.log(response.getResponseCode())
  console.log(response.getContentText())
  const access_token = JSON.parse(response.getContentText()).access_token

  return access_token
}

APIリクエストはUrlFetchApp.fetch関数を使います。詳しくは公式ドキュメントを参照してください。
次にファイルをアップロードする関数です。

// Dropboxにtxtファイルを保存する関数
function saveToDropbox(dest, content, access_token) {
  const uploadEndpoint = 'https://content.dropboxapi.com/2/files/upload'
  
  const dropbox_headers = {
    "Authorization" :"Bearer " + access_token,
    "Content-Type" : "application/octet-stream",
    "Dropbox-API-Arg" : "{\"path\":\"" + dest + "\",\"mode\":{\".tag\":\"overwrite\"}}"
  }
  
  const options =  {
      "method" : "post",
      "headers" : dropbox_headers,
      'payload' : content,
      "muteHttpExceptions" : true
    }
  
  const response = UrlFetchApp.fetch(uploadEndpoint, options)
  console.log(response.getResponseCode())
  console.log(response.getContentText())
  return response
}

完成したGASがこちら。

function myFunction() {
  // Gmailからメールを取得
  const threads = GmailApp.search('in:inbox newer_than:1d');

  // Dropbox アクセストークン取得
  const refresh_token = 'your_refresh_token'
  const app_id = 'your_app_id'
  const app_secret = 'your_app_secret'
  const access_token = getDropboxAccessToken(refresh_token, app_id, app_secret)

  // 取得したメールを処理
  threads.forEach(thread => {
    const messages = thread.getMessages();
    messages.forEach(message => {
      const subject = message.getSubject(); //メール題名
      const body = message.getPlainBody(); //メール本文
      const date = Utilities.formatDate(message.getDate(), "Tokyo/Japan", "yyyy-MM-dd") //メールの送信時間

      console.log(subject, body, date) //実行ログを出力

      // Dropbox APIでtxtファイルに保存
      const dest = "/" + subject + ".txt"
      const upload = saveToDropbox(dest, body, access_token)
    
    });
  });
}


// Dropboxにtxtファイルを保存する関数
function saveToDropbox(dest, content, access_token) {
  const uploadEndpoint = 'https://content.dropboxapi.com/2/files/upload'
  
  const dropbox_headers = {
    "Authorization" :"Bearer " + access_token,
    "Content-Type" : "application/octet-stream",
    "Dropbox-API-Arg" : "{\"path\":\"" + dest + "\",\"mode\":{\".tag\":\"overwrite\"}}"
  }
  
  const options =  {
      "method" : "post",
      "headers" : dropbox_headers,
      'payload' : content,
      "muteHttpExceptions" : true
    }
  
  const response = UrlFetchApp.fetch(uploadEndpoint, options)
  
  console.log(response.getResponseCode())
  console.log(response.getContentText())
  return response
}

// Dropboxのアクセストークンを再発行する関数
function getDropboxAccessToken(refresh, app_id, app_secret) {

  const options = {
    "method": "post",
    "headers": {
      "Content-Type" : "application/x-www-form-urlencoded"
    },
    "payload": {
      "refresh_token": refresh,
      "grant_type": "refresh_token" ,
      "client_id": app_id,
      "client_secret": app_secret,
    },
    "muteHttpExceptions" : true
  }
  
  const response = UrlFetchApp.fetch('https://api.dropbox.com/oauth2/token', options)
  
  console.log(response.getResponseCode())
  console.log(response.getContentText())
  
  const access_token = JSON.parse(response.getContentText()).access_token

  return access_token
}

このスクリプトを実行すると、Dropboxアプリコンソールで指定したフォルダにtxtファイルが保存されます。

おまけ

GASではトリガーを設定することで、スクリプトをスケジュール実行することができます。詳しくはこの記事がわかりやすいと思います。

これで定期的にGmailから特定のラベルをつけたメールがDropboxフォルダに保存されるので、T2Sアプリから読み込む(上記画像)と読み上げを聞くことができます!
Happy Automating!

この記事が気に入ったらサポートをしてみませんか?