LoginSignup
18
13

More than 5 years have passed since last update.

Google HomeからTogglを使う

Last updated at Posted at 2017-12-26

背景

タイムトラッキングサービスのToggl
大まかに言えば、「ストップウォッチで測った時間がログとして残ることで、時間の使い方を振り返ることができる」サービス。Webアプリやスマホアプリ、Windowsデスクトップアプリ等多くのプラットフォームで利用できる。
遊びと勉強のバランス取りたい的な思惑で、プライベートで利用していた。
(※本来想定されているユースケースは「デスクワーカーが仕事中に利用する」っぽい)

が…。
ボタンを押してストップウォッチを開始するのが面倒臭い。
何をしていた時間かをキーボード入力するのも面倒臭い。
→測らなくなる。

意味がない…

→Google Homeを使って、音声コマンドでストップウォッチを開始・停止できれば計測のハードルが下がるかも?

IFTTTで可能?→不可能

IFTTTのGoogle Assistantトリガーは引数を取ることができるし、TogglはWeb APIを持っているので、トリガーで聞き取った言葉を含めてアクションでAPIを叩けばすぐに実現できる、と初めは考えた。

  • IFTTTアクションで送れるリクエストは1つのみ
  • リクエストは送り捨てであり、レスポンスは扱えない
  • IFTTT アプレット同士は独立で情報の受け渡しは不可能
  • タイムエントリ停止APIを叩くにはエントリIDが必要
    • エントリIDは開始APIを叩いた際のレスポンスに含まれるが、前述の通り扱う術がない

なんの情報もないところからエントリ停止を行いたい場合、

  • 計測中のエントリ情報取得API
  • タイムエントリ停止API

の2つを叩く必要がある。

必要なAPI

Start a time entry

https://www.toggl.com/api/v8/time_entries/startにPOSTする。

Get running time entry

https://www.toggl.com/api/v8/time_entries/currentにGETする。

Stop a time entry

https://www.toggl.com/api/v8/time_entries/{time_entry_id}/stopにPUTする。

Google Apps Scriptでラッパー作成

  • タイムエントリ開始
  • 計測中のタイムエントリ停止

ができるラッパーを作成。
※例外処理もなにもない
※おおよそコピペ可能だが、startEntry関数のoptionsオブジェクトのpayloadプロパティのwidの部分は自分のワークスペースの値に置き換える必要あり。https://toggl.com/app/workspaces で特定のワークスペースの"settings"を選択してワークスペース設定画面に遷移後、URLから確認できる。https://toggl.com/app/workspaces/[ワークスペースID]/settings?のようになっている。

toggl.gs
function doPost(e){
  var jsonStr = e.postData.getDataAsString();
  var data = JSON.parse(jsonStr);
  Logger.log(data);

  var authData = Utilities.base64Encode(data.user + ':api_token');

  if(data.word != "")
  {
    var response = startEntry(data.word, authData);
  }
  else
  {
    var response = stopEntry(authData);
  }
  return ContentService.createTextOutput(response);
}

function startEntry(entryName, authData){
  var startUrl = "https://www.toggl.com/api/v8/time_entries/start";
  var options = {
    'method' : 'post',
    'headers' : {"Authorization" : "Basic " + authData},
    'contentType' : 'application/json',
    'payload' : "{\"time_entry\":{\"description\":\"" + entryName + "\",\"wid\":自分のワークスペースID,\"created_with\":\"Google Apps Script\"}}"
  }
  var response = UrlFetchApp.fetch(startUrl, options);
  return response;
}

function stopEntry(authData){
  var entry_id = getEntryId(authData);
  Logger.log(entry_id);
  var response = stopEntryById(entry_id, authData);
  return response;
}

function getEntryId(authData){
  var currentUrl = "https://www.toggl.com/api/v8/time_entries/current";
  var options = {
    'headers' : {"Authorization" : "Basic " + authData}
  }
  var response = UrlFetchApp.fetch(currentUrl, options);
  var json = JSON.parse(response.getContentText());
  return json.data.id;
}

function stopEntryById(id, authData){
  var stopUrl = "https://www.toggl.com/api/v8/time_entries/" + id + "/stop";
  var options = {
    'method' : 'put',
    'headers' : {"Authorization" : "Basic " + authData},
    'contentType' : 'application/json'
  }
  var response = UrlFetchApp.fetch(stopUrl, options);
  return response;
}

アプリの公開

GoogleドライブでGoogle Apps Scriptプロジェクトを開き、

  • 「公開」→「ウェブアプリケーションとして導入」
  • 次のユーザーとしてアプリケーションを実行:自分
  • アプリケーションにアクセスできるユーザー:全員(匿名ユーザーを含む)

ここで表示されるURLはIFTTTアプレット作成時に利用する。

ポイント?

Toggl APIの認証はBasic認証であり、

  • 認証におけるユーザー名:APIトークン
  • 認証におけるパスワード:api_token(固定文字列)

であるという点。
https://www.toggl.com/app/profile
で自分のAPIトークンを確認できる。

IFTTTアプレット作成

開始アプレットThis部 Google Assistantを選択

トリガーを引くためのフレーズ設定等はお好みで

e4127a57-c41d-46ac-98df-7ac167fbf160.png

開始アプレットThen部 Webhookを選択

URL欄にはGASを「Webアプリケーションとして導入」したときのURLをコピペして入力

Bodyには以下を入力

{ "user": "自分のAPIトークン", "word": "{{TextField}}" }

{{TextField}}にはThis部で$に相当する言葉が入り、設定したフレーズに続けた言葉がタイムエントリのタイトルになる。

d36e9a76-a41b-47cf-8c52-419cb2801ff6.png

停止アプレット

  • This部は引数なしの"Say a simple phrase"を選択しお好みで設定
  • Then部はBody以外開始アプレットと同様に設定

Bodyには以下を入力

{ "user": "自分のAPIトークン", "word": "" }

※上記のラッパーはJSONのword属性が空かどうかで開始か停止かを判断している。IFTTTアプレットのThis部の設定がまずいと、開始したいのに停止のJSONが送られることがある点には注意。

開始と停止のアプレットを保存すれば、呪文を唱えて開始・停止できるようになる。

ちょっとした問題

タイムエントリはタグやプロジェクト情報を持つことができるが、今回作った仕組みではそこに触れないので、エントリを分別したい場合あとでタグ付け等しないといけない。

余談:なぜ「計測中のエントリを停止」APIが公式にないのか?

「計測中のエントリを停止」APIを用意してくれていれば今回の目的はIFTTTを使うだけで済ませられた。
なぜないのか?
→あるとステートフルになってしまう

「計測中のエントリを停止」APIがあるとすると、クライアント側は今計測中かどうか気にせず叩ける代わりに、サーバー側では状態に応じて振る舞いを変えなければならない(ステートフル)。

Toggl APIの隅々まで検証したわけではないが、REST APIとして設計されているのだとすれば、ステートレスであることが必要になるため「計測中のエントリ取得」「IDを指定して計測停止」の2つのAPIに分かれているのは自然なことである。

18
13
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
13