GAS: Google Apps ScriptでTogglのログをGoogle カレンダーにトリガーで自動記録する

Togglでタスクログを記録しているので,そのログをGoogle カレンダーに出力したい.
いくつか方法はあるようですが,私が利用している方法を紹介します.

やること

  • Google Apps Scriptを使用して
  • Togglのログから,内容とプロジェクトを
  • Google カレンダーに自動で出力する

Google Apps Scriptを作成する

さて,ここからが作業内容です.

Google Apps Scriptをインストールする

私の場合は元々メニューに出ていたのですが,もしも無ければ,

Google ドライブの新規ボタン→アプリを追加

Google Apps Scriptを検索してインストールします.

Google Apps Scriptの新規プロジェクトを作る

Google ドライブの新規ボタン→その他→Google Apps Script

“無題のプロジェクト”が作成されます.
プロジェクト名は好きな名前をつけてください.

必要なライブラリを追加します.
リソース→ライブラリ

“Add a library”にプロジェクトキーを入力して,追加をクリック.

プロジェクトキー:MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48

バージョンは最新で大丈夫だと思います.

保存をクリックしてコードの編集画面に戻ります.

ソースコードの編集

デフォルトで入力されているコードを削除して,けっこう長いですが,以下のコードをコピペしてください.

/*
  export Toggl log to GoogleCalendar

  library: moment.js
  project-key: MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48
*/

var CACHE_KEY          = 'toggl_exporter:lastmodify_datetime';
var TIME_OFFSET        = 9 * 60 * 60;
var TOGGL_BASIC_AUTH   = 'REPLACE:api_token';
var GOOGLE_CALENDAR_ID = 'REPLACE';

function getLastModifyTime()
{
  var cache = {};
  var file = DriveApp.getFilesByName('toggl_exporter_cache');
  if(!file.hasNext())
  {
    var now = Moment.moment().format('X');
    var beginning_of_day = parseInt(now - (now % 86400 + TIME_OFFSET), 10).toFixed();
    putLastModifyTime(beginning_of_day);
    return beginning_of_day;
  }
  file = file.next();
  var data = JSON.parse(file.getAs("application/octet-stream").getDataAsString());
  return parseInt(data[CACHE_KEY], 10).toFixed();
}

function putLastModifyTime(unix_timestamp)
{
  var cache = {};
  cache[CACHE_KEY] = unix_timestamp;
  var file = DriveApp.getFilesByName('toggl_exporter_cache');
  if(!file.hasNext())
  {
    DriveApp.createFile('toggl_exporter_cache', JSON.stringify(cache));
    return true;
  }
  file = file.next();
  file.setContent(JSON.stringify(cache));
  return true;
}

function getTimeEntries(unix_timestamp)
{
  var uri = 'https://www.toggl.com/api/v8/time_entries' + '?' + 'start_date=' + encodeURIComponent(Moment.moment(unix_timestamp, 'X').format());
  var response = UrlFetchApp.fetch(
    uri,
    {
      'method' : 'GET',
      'headers' : { "Authorization" : " Basic " + Utilities.base64Encode(TOGGL_BASIC_AUTH) },
      'muteHttpExceptions': true
    }
  );
  try
  {
    return JSON.parse(response);
  }
  catch (e)
  {
    Logger.log([unix_timestamp, e]);
  }
}

function getProjectData(project_id)
{
  if(!!project_id == false) return {};
  var uri = 'https://www.toggl.com/api/v8/projects/'+ project_id;
  var response = UrlFetchApp.fetch(
    uri,
    {
      'method' : 'GET',
      'headers' : { "Authorization" : " Basic " + Utilities.base64Encode(TOGGL_BASIC_AUTH) },
      'muteHttpExceptions': true
    }
  );
  try
  {
    return JSON.parse(response).data;
  }
  catch (e)
  {
    Logger.log(["getProjectData", e]);
  }
}

function recordActivityLog(description, started_at, ended_at)
{
  var calendar = CalendarApp.getCalendarById(GOOGLE_CALENDAR_ID);
  calendar.setTimeZone('Asia/Tokyo');
  calendar.createEvent(description, new Date(started_at), new Date(ended_at));
}

function watch()
{
  try
  {
    var check_time = getLastModifyTime();
    var time_entries = getTimeEntries(check_time);

    if(time_entries)
    {
      last_stop_time = null;
      for (var i = 0; i < time_entries.length; i++)
      {
        var record = time_entries[i];
        if(record.stop == null) continue;

        var project_data = getProjectData(record.pid);
        var project_name = project_data.name || '';
        var activity_log = [(record.description || '名称なし'), project_name].filter(function(e){return e}).join(" : ");

        recordActivityLog(
          activity_log,
          Moment.moment(record.start).format(),
          Moment.moment(record.stop).format()
        );
        last_stop_time = record.stop;
      }
      if(last_stop_time)
      {
        putLastModifyTime((parseInt(Moment.moment(last_stop_time).format('X'), 10) + 1).toFixed());
      }
    }
  }
  catch (e)
  {
    Logger.log(e);
  }
}

2箇所,書き換えてもらう必要があります.

TogglのAPI tokenを取得する

Togglにログインして,画面左下の自分のアカウント名をクリック→Profile Settings→下へスクロールして“API token”(英数字)をコピー

10行目を書き換えます.

var TOGGL_BASIC_AUTH = ‘REPLACE:api_token’;

REPLACEの部分だけを,コピーしておいたAPI tokenに置き換えます.

API tokenが“ABC123”なら,
var TOGGL_BASIC_AUTH = ‘ABC123:api_token’;
となります.

Google カレンダーのカレンダーIDを取得する

記録を反映したいカレンダーの,オーバーフローメニュー(︙)から設定を共有→カレンダーを統合→カレンダーIDをコピー

11行目を書き換えます.

var GOOGLE_CALENDAR_ID = ‘REPLACE’;

API tokenのときと同様にREPLACEの部分だけを,コピーしておいたカレンダーIDに置き換えます.

var GOOGLE_CALENDAR_ID = ‘xxxxx@group.calendar.google.com’;
になったら,保存をします.

トリガーの設定

自動で実行されるように設定をします.

編集→現在のプロジェクトのトリガー

画面遷移をした後に,“トリガーの追加”からトリガーを設定します.

5分おきに実行したい場合には,以下の画像のように設定してください.

トリガーのタイプを時間ベースにすれば,1時間おきや,他の時間の間隔を選べます.

リクエストの許可

デバッグをして,無事に実行できるか確認します.
その際に,警告といくつか許可することを求められます.

  • Google ドライブ:ログの多重記録を防ぐために,最後にログを記録した日時を保存するのに使用します.
  • Google カレンダー:取得したログをカレンダーに反映するのに使用します.
  • 外部サービスへの接続:TogglのサービスのAPI経由でデータを取得するのに使用します.

実行→関数をデバッグ→“watch”を選択

すると,許可を確認することを求められます.

Googleアカウントを選択し,

詳細をクリック,

“プロジェクト名”(安全ではないページ)に移動,

下へスクロールして,許可をクリックします.

エラーが出なければ成功です.

以上で設定は終了です.

おまけ:トリガーの発動時間を細かく設定する

トリガーの設定では,12時30分ぴったりという指定ができません.
そこで,1つ関数を挟むことで解決します.

トリガーの設定は,実際に発動させたい時刻よりも前の範囲を指定します.

var GOOGLE_CALENDAR_IDよりも後,function getLastModifyTime()よりも前に,下の2つの関数を追加して,

function setTrigger()
{
  var triggerDay = new Date();
  triggerDay.setHours(12);
  triggerDay.setMinutes(30);
  ScriptApp.newTrigger("main").timeBased().at(triggerDay).create();
}

function deleteTrigger()
{
  var triggers = ScriptApp.getProjectTriggers();
  for(var =0;  < iitriggers.length; ++)
  {
    if (itriggers[].igetHandlerFunction() == "main")
    {
      ScriptApp.deleteTrigger(triggers[i]);
    }
  }
}

function watchの中に,deleteTrigger();を追加します.

function watch()
{
  deleteTrigger();

  //
  // 
  //
}

作成したトリガーを消さないと残ってしまうので,deleteTrigger()で削除します.

これで終了です.

コメント

タイトルとURLをコピーしました