freeeに関係ない話題ですが、Google Apps Script(以下GAS)でできる効率化の例として番外編としてfreee API以外のGASでの活用に関しても記事にしています。
今回は、「Gmailでスターを付けたメールからTodoistのタスクを作成し、メールの受信をSlackに通知する」に挑戦したいと思います。
前回の記事では未処理のスター付きメールのみを取得しました。
Todoist APIの公式ドキュメントを確認する
APIを使用してTodoistにタスクを作成するために、まずTodoist APIの認証を通す必要があります。いつものように公式ドキュメントの確認からスタートします。
Todoist APIには、REST APIというものとSync APというものがあるようです。
公式ドキュメントによると
Todoist REST API は、Todoist ウェブサービス上でデータを読み書きする最もシンプルな方法を提供します。
ほとんどの一般的なアプリケーション要件に対して、外部開発者にお勧めの API で、RESTful API を呼び出した経験のある方なら誰でも知っているようなアプローチを採用しています。
https://developer.todoist.com/guides/#our-apis
とあるので、とりあえずREST APIを使用します。
Authorizationをキーワードに認証方法を確認する
海外のサービスのドキュメントは英語であることがほとんどですが、ここが日本人にとってのハードルとなることが多いです。
今はページ翻訳などの拡張機能がありますので、そういったものを利用するとよいですが、翻訳ツールによる微妙な誤訳がドキュメントを読みにくくもします。
ということで、英語のまま定番のキーワードを探して、その部分を都度翻訳する方が必要な情報を見つけやすいように思います。
APIに関しては、まず認証からスタートするので、Authorizationをキーワードにドキュメントを検索します。

REST API を認可して呼び出すには、アプリケーションから適切な Bearer $token を含む認可ヘッダを提供する必要があります。この例では、アカウントのintegrations settingsから個人用の API トークンを取得します。
https://developer.todoist.com/rest/v1/#authorization
公式ドキュメントによると、RESET APIの認証に必要なトークンは、利用しているアカウントのintegrations settingsのページに記載があるようです。このトークンを使ってBeare認証(リクエストヘッダに ‘Authorization’: 'Bearer アクセストークン
‘ を加えてリクエストする)を行うようです。
APIトークンで認証をテストしてみる
ドキュメントのintegrations settingsをクリックするとTodoistの設定 – 連携機能のページに遷移します。その下部にAPIトークンの記載があります。

このAPIトークンを用いてテストリクエストを送るために、公式ドキュメントのcurlコマンドの例を確認します。

curlコマンドは慣れないと何を書いているのかがわかりませんが、Convert curl commands to codeというサイトを使って任意のプログラミング言語に変換できます。今回は、JavaScriptに変換してみました。
このJavaScriptは、そのままGASでは使用できませんので、読み替える必要があります。
このあたりに関しては、以下の記事で詳しく紹介しています。
ということで、APIトークンを利用した認証をテストしてみます。
function todoistAuthByToken() {
const token = '自分のAPIトークンをここに書く';
const url = 'https://api.todoist.com/rest/v1/projects';
const params = {
headers: { 'Authorization': `Bearer ${token}` }
};
const response = UrlFetchApp.fetch(url, params);
const json = response.getContentText();
const obj = JSON.parse(json);
console.log(obj);
}

無事リクエストが成功しました。
先々の事を考えてOAuth認証が推奨
Todoistをチームでなく個人で使っている場合には、先述のトークン認証で問題ないと思います。しかし、チーム利用や操作権限を限定して事故を防ぎたいといった事を考えた場合、OAuth認証で利用するほうが良いでしょう。
公式ドキュメントでもOAuth認証を推奨しています。

他のユーザーを認証するために、アプリケーションはOAuthプロトコルを使用して彼らからトークンを取得する必要があります。OAuthを使用して弊社サービスからトークンを取得する方法については、認証ガイドを参照してください。
https://developer.todoist.com/rest/v1/?shell#authorization
OAuth認証のガイドを確認する
公式ドキュメントには、
外部アプリケーションは、OAuth2 プロトコルを介して、ユーザー認証された API トークンを取得できます。始める前に、開発者はアプリ管理コンソールでアプリケーションを作成し、有効な OAuth リダイレクト URL を設定する必要があります。登録された Todoist アプリケーションには、OAuth2 フローに必要な固有の Client ID と Client Secret が割り当てられます。
https://developer.todoist.com/guides/#authorization
と記載があり、この認証プロセスは、GASでfreee API を利用する場合と同じです。

GASでのOAuth認証の定番の流れは以下となります。
- サービス側で操作権限設定したアプリを作成
- リダイレクト(コールバック)URLにはGASのスクリプトIDを含むコールバックURLを指定
- GAS側にOAuth2ライブラリを追加
- 割当られたClient ID と Client Secretを用いて認証
Todoist側のアプリの作成・設定をする
まず公式ドキュメントに記載されているApp Management Console をクリックすると以下のアプリ作成画面に遷移します。

ここで、Create a new appをクリックして新しいアプリを作成します。

App nameに任意のアプリ名を入力します。App service URLはGASでの利用の場合は入力不要です。

続いて
- お好みでアイコンを設定(300px x 300px)
- Client ID, Client secreを控える
- GASのスクリプトIDを含んだリダイレクト(コールバック)URLを入力する
- Save settingsをクリック
でTodoist側のアプリの設定は完了です。
リダイレクト(コールバック)URLに関しては、GASのスクリプトIDが必要になります。スクリプトIDは、左側の歯車アイコンをクリックしてプロジェクトの設定を表示すると、中段で確認できます。

このスクリプトIDを以下の定型のURLに組み込んで、Todoist API のアプリ設定画面のOAuth redirect URLに入力します。
https://script.google.com/macros/d/スクリプトID/usercallback
OAuth2ライブラリを追加する
続いてGAS側のプロジェクトファイルにOAuth2ライブラリを追加します。
まずスクリプトエディタの画面の左側にあるライブラリの項の+ボタンを押します。

ライブラリの追加という画面が表示されますのでOAuth2ライブラリのスクリプトIDを入力して検索ボタンを押します。

// OAuth2ライブラリのスクリプトID
1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
ライブラリのバージョン選択の画面が表示されたら、通常最新のバージョンがデフォルトで選択されていますので、そのまま追加ボタンをクリックします。

これでOAuth2ライブラリが追加されました。

OAuth2ライブラリを使用した定番の認証用関数を設定する
OAuth2ライブラリを使用した定番の認証用関数のセットがあります。
- authTodoist() – 最初の認証コードを取得する関数
- getTodoistService() – OAuth2ライブラリのメイン関数
- authCallbackTodoist(request) – 認証が成功したかの判定用の関数
- logTodoistAccessToken() – アクセストークンが取得できるかのテスト用関数
/**
* OAuth2ライブラリ
* 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
*
* Google Apps Script リダイレクトURL
* https://script.google.com/macros/d/スクリプトID/usercallback
*
* Todoist公式リファレンス
* https://developer.todoist.com/guides/#developing-with-todoist
*
*/
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* Todoist認証用関数 */
function authTodoist() {
//認証URLを取得
const authUrl = getTodoistService().getAuthorizationUrl();
console.log(authUrl);
}
/* Todoist APIのサービスを取得する関数 */
function getTodoistService() {
const client_id = PropertiesService.getScriptProperties().getProperty('CLIENT_ID_TODOIST');
const client_secret = PropertiesService.getScriptProperties().getProperty('CLIENT_SECRET_TODOIST');
return OAuth2.createService('Todoist')
.setAuthorizationBaseUrl('https://todoist.com/oauth/authorize') // 基本認証リクエストURL
.setTokenUrl('https://todoist.com/oauth/access_token') // トークン取得リクエストURL
.setClientId(client_id)
.setClientSecret(client_secret)
.setCallbackFunction('authCallbackTodoist')
.setPropertyStore(PropertiesService.getUserProperties())
.setScope('data:read_write') // タスクの取得と追加の権限
}
/* 認証コールバック関数 */
function authCallbackTodoist(request) {
const service = getTodoistService();
const isAuthorized = service.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('認証に成功しました。タブを閉じてください。');
} else {
return HtmlService.createHtmlOutput('認証に失敗しました。');
};
}
/* アクセストークンが取得できるかテストする関数 */
function logTodoistAccessToken() {
const accessToken = getTodoistService().getAccessToken();
console.log(accessToken);
}
まず最初に設定するのが、getTodoistService()関数です。
- 利用するサービス名
- クライアントID
- クライアントシークレット
- 初期認証コードリクエストURL
- アクセストークンリクエストURL
- アプリの権限スコープ
などを設定します。
まず、クライアントIDとクライアントシークレットはプロパティストアに格納しておいたものを呼び出して定数に代入します。
const client_id = PropertiesService.getScriptProperties().getProperty('CLIENT_ID_TODOIST');
const client_secret = PropertiesService.getScriptProperties().getProperty('CLIENT_SECRET_TODOIST');
同じスクリプト内で複数のAPIを利用する場合もあります。そういった場合も想定して、サービス名を任意で付けておけます。
OAuth2.createService('Todoist')
続いて、初期認証コードリクエスト用のURLとアクセストークンを取得するためのURLを設定します。これらは公式ドキュメントから確認します。
.setAuthorizationBaseUrl('https://todoist.com/oauth/authorize')
.setTokenUrl('https://todoist.com/oauth/access_token')


セキュリティが厳しいAPIの場合は、取得したアクセストークンに利用期限が設定されており、そのアクセストークンを更新するためのエンドポイントなどもありますが、Todoistはアクセストークンの期限が設定されていないようです。
最後にこれがOAuth2ライブラリを使用した認証での最重要パートですが、パーミッションスコープ(アプリの権限スコープ)の設定を行います。
これもまず公式ドキュメントを確認します。

- task:add – タスクの追加
- data:read – タスクの取得
- data:read_write – タスクの追加と取得
- data:delete – タスクの削除
- project:delete – プロジェクトの削除
今回作成するスクリプトに与えたい権限を検討して、入力します。今回は、data:read_write – タスクの追加と取得を選択します。
.setScope('data:read_write') // タスクの取得と追加の権限
OAuth2ライブラリのスコープの設定に関しては、以下の記事が非常に参考になります。
いつも隣にITのお仕事|OAuth2 for Apps Scriptライブラリを使ってサービスを作るスクリプトを解説
OAuth認証を行いアクセストークンを取得する
いよいよ実際に認証をしていきます。まずauthTodoist()関数を実行します。

ここでログ出力されたURLをブラウザに貼り付けます。

アプリと権限の設定を確認して利用に同意します。

authCallbackTodoist(request)関数が自動実行され、「認証に成功しました。タブを閉じてください。」とブラウザに表示されれば認証成功です。
最後にlogTodoistAccessToken()関数を実行して、アクセストークンがログ出力されれば完了です。

このログ出力する関数をちょっとアレンジして、アクセストークンを戻り値として返す関数にしておくと、後で他のスクリプトで利用できて便利です。
/* アクセストークンを戻り値として返す関数 */
function getTodoistAccessToken_() {
const accessToken = getTodoistService().getAccessToken();
return accessToken;
}
おわりに
今回は、「Todoist API をOAuth認証しアクセストークンを取得する」を紹介しました。
OAuth2ライブラリを利用したAPI認証は、様々なサービスで利用しますので、何度もトライして慣れておくのがオススメです。とくに権限スコープの設定は、サービスによって必要であったり、不要であったりするので、なかなか習得しづらいポイントです。利用したいAPIのドキュメントをよく読み込んで設定しましょう。
シリーズ目次
- Gmailのスター付きメールからTodoistのタスクを作成&Slackに通知する その1 – isStarred()メソッドを使って、未処理のスター付きメールのみを絞り込む
- Gmailのスター付きメールからTodoistのタスクを作成&Slackに通知する その2 – Todoist APIの認証を通す
- Gmailのスター付きメールからTodoistのタスクを作成&Slackに通知する その3 – Todoist APIでタスクを新たに作成する
- Gmailのスター付きメールからTodoistのタスクを作成&Slackに通知する その4 – Incoming Webhooksを使ってSlackに通知
Google Apps Scriptを勉強したい方へ
この記事を見て、GASを勉強したいなと思われた方はぜひノンプログラマーのためのスキルアップ研究会(通称 ノンプロ研)にご参加ください。私も未経験からこの学習コミュニティに参加し、講座を受講したことでGASが書けるようになりました。
学習コミュニティ「ノンプログラマーのためのスキルアップ研究会」
挫折しがちなプログラミングの学習も、コミュニティの力で継続できます。ノンプロ研でお待ちしております!