Todoistで完了したタスクをEvernoteに溜めていくお話

最近、Todoistを使ってTodoを管理するようになったけど、いつ何を終えたかの記録が欲しい時がある。IFTTTとかZapierを使えばそれなりにはできなくもないけど、ちょっとなんかアレ(日時時間の表示とか)。。

ということで、Todoist APIとEvernote API使って自分の使いやすいようになんとかしてみる。
Todoistに関しては、Webhooksを使って完了したタスクを取得するようにする。

基本構成

作りたいアプリケーションとしては、Todoistでタスクが完了したらそれをEvernoteのノートに書き込む。このノートの単位としては1日単位で作成する。
なので、Todoist側は完了したタスクをひたすら流すだけで、Evernote側はその日のノートがあれば完了したタスクを追加で書き込んで、もしなければ新規にノートを作成して完了したタスクを書き込むということをするだけ。
こう書くだけだとめっちゃ簡単そう。(実際やると色々ハマります。。)
イメージはこんな感じでTodoistでチェックしたタスクがEvernoteのその日のノートに溜まっていく感じ(このノートは実際に自動で作ったやつ)。

Todoist

Webhookを使うためにはまずTodoist App Managementで登録する必要があるので、登録する。ここのページからCreate a new appでアプリ名とURLを入れたらオッケー。
Image from Gyazo
無事にアプリが作成できると、Webhooksのもう少し詳しい設定ができるので、callback URLとどういうEventがきた時に動作するかを設定する。自分の場合はタスクが完了した時なので、item:completedにチェックを入れる。
Image from Gyazo
他にも情報的には、暗号化に使うClient secretののKeyなどもあるけど割愛する。
これでTodoist自体の設定はできたのでcallback用のプログラムを実装する。

LINE botを作る訳ではないが、PythonでWebhook周りを実装するのを調べていると「Python + HerokuでLINE BOTを作ってみた」というのがあったのでこれを参考にしながら。

from flask import Flask, request

@app.route("/callback", methods=['POST'])
def callback():
    body = request.get_json()
    content = body["event_data"]["content"] # Taskの名前

楽ちん。
Todoistでタスクが完了したら、こっちにPOSTが投げられてきて動作して、こんな感じのが得られる。

{"event_name":"item:completed","initiator":{"is_premium":true,"image_id":"a0f7b9baf8f9a2d7266d4eb293e4ef26","id":16237325,"full_name":"hogehoge","email":"hogehoge@fugafuga.com"},"version":"8","user_id":123456,"event_data":{"is_deleted":0,"assigned_by_uid":null,"labels":[],"sync_id":123456,"section_id":null,"in_history":1,"child_order":7,"date_added":"2020-05-09T08:55:36Z","checked":1,"id":123456,"content":"Webhook test","date_completed":"2020-05-09T13:11:20Z","added_by_uid":123456,"user_id":123456,"url":"https://todoist.com/showTask?sync_id=123456&id=123456","due":null,"priority":1,"parent_id":null,"responsible_uid":null,"project_id":123456,"collapsed":0}}

必要に応じて必要な情報を取り出すので良して色々実装可能。
タスクがTodoistのどのプロジェクトのものか知りたければ、REST APIと組み合わせてやればオッケー。

import uuid, requests, json

project_id = body["event_data"]["project_id"]
response = requests.get("https://api.todoist.com/rest/v1/projects/%s" % project_id, headers={"Authorization": "Bearer %s" % API_TOKEN}).json()
project_name = response["name"]

REST APIとかはここ参照。
Todoist側はこんな感じで完了。
自分が作ったやつは一応LINE botのSignatureValidatorを参考にして、本当にTodoistからのものかどうかは確認するようにしている。

Evernote

Developer用のsandboxでテスト

開発用のsandoxがあるのでまずはこっちでアカウント作る。アカウントにサインインした状態でこのURLにアクセスするとDeveloper Tokenが得られるのでこれを使って開発していく。
pipでインストールできるevernoteのライブラリはPython2にしか対応していないというヤバい感じなので、GitHubで公開されているPython3のβ版を使う。
基本構成に書いたように、基本的にはノートがあるかチェック、新規書き込み、追加書き込みができればオッケー。
ノートがあるかのチェックに関しては、公式のドキュメントブログ記事を参考に実装する。

import evernote.edam.notestore.ttypes as NoteStore

note_filter = NoteStore.NoteFilter()
note_filter.words = '' 
note_filter.ascending = False # 降順
note_filter.notebookGuid = "xxxxxx" # notebookのguid
notes_metadata_result_spec = NoteStore.NotesMetadataResultSpec()

notes_metadata_list = note_store.findNotesMetadata(note_filter, 0, 10, notes_metadata_result_spec) 

title = "Title"
for note_guid in notes_metadata_list.notes:
    note = note_store.getNote(note_guid.guid, True, False, False, False)
    if note.title == title:
        上書き()
    else:
        新規作成()

こんな感じでひとまずはなんとかなる。note_filter.notebookGuidに関しては予め調べる必要があるので注意。あとnote_filter.wordsの部分に関してはここのを色々試したけど結局上手くできなかった。原因は気になるが今回の用途的には実質最新のものだけで十分なので、上にあるように何も入れずに検索するのでよしとした。

ノートがある場合とない場合の処理は下の感じ。完了したタスクを書き込みたいタイトルのノートがあれば上書きしbreakする。既定の数見てなければ最後に新規にノートを作成するという感じ。ノートの中身の抜き出しは正規表現でやってしまった。なんかもっといい方法ありそうだけど。。

import re

title = "TITLE"
note_header = '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'
content_start = '<en-note>'
content_end = '</en-note>'
# '<en-note>'と'</en-note>'に囲まれている部分を後方参照で取り出すのに使う
content_ext_p = re.compile("%s(.*?)%s" % (content_start, content_end))

has_note = False # 同じタイトルのノートがあるかのフラグ
for note_guid in notes_metadata_list.notes:
    note = note_store.getNote(note_guid.guid, True, False, False, False)
    if note.title == title:
        m = content_ext_p.search(note.content)
        if m:
            content = m.group(1)
            content += "<div>本文</div>"
            note.content = note_header
            note.content += content_start
            note.content += content
            note.content += content_end
            note_store.updateNote(note)
        has_note = True 
        break
# ノートがない場合
if not has_note:
    note = Types.Note()
    note.title = title
    note.content = note_header
    note.content += content_start
    note.content += "<div>本文</div>"
    note.content += content_end
    note.notebookGuid = "xxxxxx"  notebookのguid
    note_store.createNote(note)

これでsandbox上でEvernoteに書き込むところも動くことが確認できた。

Evernote API

APIを使うにはAPI Keyが必要なのでまずはその登録をする。
開発者用のページ?にいくと、右上の方にGET AN API KEYというのがあるのでそこ行く。そうすると下記のようなフォームがあるので適当に埋めて送信。
Image from Gyazo
そうすると一瞬で登録できて下記のようにConsumer KeyConsumer Secretが発行されるがまだ使えない。
Image from Gyazo
sandboxで開発が終われば、https://dev.evernote.com/support/ にアクセスして、API Keyのアクティベートする。

こんな感じの画面になるので、上のAPI Keyを発行する時に入れたApplicationの名前や発行されたKeyを入力していく。アクティベート完了のメールが来たら使えるらしい。

TodoistとEvernoteの連携

Todoistの部分とEvernoteの部分ができているのであとはつなげるだけ。Todoistで完了したら、その中身を解析してそれをEvernoteに書き込む。楽ちん。
再掲だけどできるとこんな感じで自動で溜めていけるようになる。(完)

コメント (1)

コメントを残す