2018年8月18日土曜日

Google Apps ScriptでGoogleカレンダーとTODOリストを連携する

動機

最近、Google謹製のTODOリストを使っている。シンプルなのは非常にいいのだが、いくつか使いづらい点がある。そのうちの一つに、時間指定のタスク登録・通知ができないという点がある。そこで、Google Apps Scriptの勉強もかねて、GoogleカレンダーとGoogle TODOリストを連携させて、時間指定の通知をできるようにしたいと思う。

そんなのカレンダーに直接登録すればいいじゃん!という声も聞こえそうだが、TODOリストへの登録のしやすさは捨てがたいので、こういう面倒なことを思いつくのであった。

また、もう一つ強力なモチベーションとして、このスマートウォッチの購入がある。
NOKIA製のスマートウォッチ!ただ、こいつが通知してくれるのは、
  • 電話
  • メール
  • カレンダー
の三点に限られており、TODOをカレンダーに登録するのは必然なのであった。

実践

やりたいことをより具体的にすると、TODOリストに日限(時刻含む)が記載されているタスクを、Googleカレンダーの指定の時刻に登録、ということなので、Google謹製ソフトの連携だけなので簡単じゃん!…とはいかず。曲者はTODOリストのほうである。公式のReferenceは下記。
Google謹製のくせに標準でGoogle Apps Scriptのクラスには登録されておらずハブられているし、実行しようにもひと手間加えてあげないといけないという面倒なやつ。公式Referenceも英語だけなのはまだしも、肝心のGASベースでの記述方法が書いておらず、非常に使いづらい…

ということで行きついたのが下記のサイト
Tasksの使いはじめの設定から、リストの取得や変更まで詳しく書いてあり、このページでほとんど事足りるのだが、いかんせん作成されたのが2012年で、このころと仕様が若干変わっているらしく、命令などもちょっと違っている。

ということで、実際に書いたコードが下記である。
function tasksToCalender() {

  //defaultのタスクリスト読み込み
  var listitem = Tasks.Tasklists.list().items;
  var taskListId = listitem[0].id;
  var tasks = Tasks.Tasks.list(taskListId);

  //全タスクについて判定
  if (tasks.items) {
    for (var i = 0; i < tasks.items.length; i++) {
      
      //タスク読み込み
      var task = tasks.items[i];
      
      //日付判定。タスクの日付が今日以前もしくは期日設定なしの場合は処理終了
      var date = new Date(tasks.items[i].due);
      var today = new Date();
      if (date.getTime() < today.getTime() || !date.getTime()) {
        continue;
      }
      
      //なぜかnotesでStringの関数が使えないため変換
      var str = String(task.notes);
      
      //@がnotesにある場合はgoogleカレンダーに登録する
      if (str.match('@')) {

        //日本時刻に戻すために9時間引いた値を時間に設定
        date.setHours(date.getHours() + Number(str.substr(1,2))-9)

        //startTimeはさらに分を足して設定。アラーム目的なのでendTimeは+1hour
        var startTime = new Date(date.setMinutes(date.getMinutes() + Number(str.substr(3,2))));
        var endTime = new Date(date.setHours(date.getHours() + 1));
        CalendarApp.createEvent('TEST', startTime, endTime);
        
        //@を消して二重登録を防ぐ
        task.notes = str.replace('@','');
        Tasks.Tasks.update(task, taskListId, task.id);

      };
    }
  }

}

タスク登録は、下記のように詳細部分に@時間、のように指定する。
こうすることで、指定日時のカレンダーに登録されるようになる。

詳細

ちょっと面倒なところだけ説明を加えようかと思う。

1. TODOリストの取得
var listitem = Tasks.Tasklists.list().items;
var taskListId = listitem[0].id;
var tasks = Tasks.Tasks.list(taskListId);
やっていることは単純なんだが、先のリンクにも書いてある通り、まずはTasklistsでTODOリストのタスクリストを配列として取得して、その中から目的のタスクリスト(今回はdefaultなので配列の0番目)のidを取得、その上でタスクリスト内のタスクを配列で取得、となんとも回りくどいことをしないといけない。

2. 時間の判定
var str = String(task.notes);
      
if (str.match('@')) {

  … …

}
この1行目が曲者で、公式さんはTasksのnotesプロパティはStringだよ!!と言っているにもかかわらず、なぜかStringに使用できるmatch他のメソッドが使用できないというエラーが出るため、ここで強制的にString型に変換している。何とも不思議である。一体何型が返ってきているのだろうか??

で、まあ予定通りに@がついているものだけ処理するif文を入れてます。

3. タスクのupdate
task.notes = str.replace('@','');
Tasks.Tasks.update(task, taskListId, task.id);
タスクの2重登録を防ぐために、指定タスクのnotesから@を消す作業。この2行目のupdateメソッドについて、引数の数やらなにやらが公式さんに書いておらず(書いてあるのかもしれないがわからなかった)、下記のGAS Editorの補完から推定するしかなかった。
Tasks.Tasks.update(Task resource, String tasklist, String task)
これは、変更を加えたTaskオブジェクトを、あるタスクリストのあるタスクと置き換える、という意味だ(と思われる)。なので、1行目でtaskのnotesの@を削除して第1引数に(なぜかnotesにはStringが代入できる…!!)、タスクリストのidとタスクのidを2, 3引数にぶち込んでやることで、無事にタスクが置き換わった。

まとめ

ということで、連携完了。コード自体はすごく単純なんだが、教科書が頼りにならず、ググってもこんなマニアックなことやろうとする人はいないらしく検索できず、ほぼ自力で作成したので時間がかかってしまった。あとはこいつを適当な時間タイミングでリピートすればOKということですな。

デバッグ不十分なため、ミスあるかも。

0 件のコメント:

コメントを投稿