TickTickで完了したTaskを自動的にNotionのデータベースに登録する仕組みを構築します。
概要
Notionはノートの作成やカンバンによる管理、データベースの構築などオールインワンに情報を管理できます。データベースを使ったGUIでフローを構築したり、情報をストックできたりする点にメリットがある一方で、特定の目的に特化したツールを使うほうが時には高い利便性を得られることもあります。
今回はToDoの管理をTickTickを用いつつ、完了したTaskをNotionに蓄積させて集計で振り返りができるように機能をつくります。
やること
TickTickで完了したタスクをNotion上のデータベースに蓄積します。大まかな流れは以下の通りです。
- TickTickでタスクの完了をする
- IFTTTでタスクの完了を検知してGASへWebhook(POST)
- GASでNotionの認証と値の変換をしてWebhook(POST)
- Notionのデータベースに完了したタスクを挿入する
イベントの発火にはIFTTTのようなWebサービスをつなぐツールが便利なので、これを使います。IFTTTだけではWebhookのHeaderに認証情報を加えたり値を変換したりが難しいので、このギャップをGASで埋めます。
Notion APIを使う準備
Integrations>手順を参考に、Notion側でAPIを使えるように準備します。
データベースの構築
TickTickからIFTTTでWebhookにのせられる情報は上の通りです。すべてのデータを保持することに現状ではデメリットはないので、上の情報をストックできるデータベース(テーブル)をNotion上につくります。
日付系の列はDate型、TagはMultiSelect型、それ以外はString型に設定します。データベースの構築が完了したら、ページ右上のShareから作成したIntegrationsをInviteしてAPIを使えるようにしてください。
GASの構築
HTTP-POSTのテスト
[FYI]IFTTTのWebhookをGASのPOSTで受け取るときのパラメータ覚書
上を参考にIFTTTからWebhookを受け取るコードをためしに書きます。Google Apps ScriptのEditorを開き、以下のコードを入力します。
function doPost(e) {
var jsonString = e.postData.getDataAsString()
const options = {name: "webhookTest"}
GmailApp.sendEmail("yourAlias@gmail.com", "webhookTest", jsonString, options)
}
yourAlias@gmail.com
のyourAlias
は使っているGoogleアカウントのIDなど、任意のアドレスに書き換えます。doPost
関数でPOSTをためしに受け取るため、APIをデプロイします。
- [デプロイ]をクリック
- [新しいデプロイ]をクリック
- [種類の選択]をクリック
- [ウェブアプリ]をクリック
- フォームに入力する
- 説明文を書く
- e.g. Receive a webhook from IFTTT
- 次のユーザーとして実行で自分を選ぶ
- アクセスできるユーザーを「全員」にする
- 説明文を書く
- [デプロイ]をクリック
- ウェブアプリのURLをコピーしてメモ帳などにペースト
手順が完了したら、ARCやIFTTTなどを使い、コピーしたURL宛にHTTP-POSTを投げてみてください。POSTのRequest内容が上記で設定したメールアドレスに送信されます。うまくいかない場合には、デプロイの設定やコードの誤りなどご確認ください。
ライブラリの導入
Notion APIを操作するライブラリを導入します。ソースコードは以下の通りです。
Error処理などは省きましたが、notion-sdk-pyと公式ドキュメントを参考に書いたものです。
公式のライブラリをGAS用に作り直してくださっている方もいるかもなので、notion-sdk-gs
を使う前に再利用できるリソースがないかご確認ください。また、APIのアップデートで機能が充実する可能性もあるので、公式ドキュメントもなるべくご覧ください。
以下、notion-sdk-gs
の導入手順です。
- GAS Editorの左側のサイドメニューから[ライブラリ]をクリック
- スクリプトIDに以下を入力
- 1C6kLuU1Ugclg-8C7hsbK221IgepJoGTrbh2VI4itdExvyFDCzc4adK8h
- [検索]をクリックする
- IDに「Notion」と入力する
- [追加]をクリックする
追加が完了するとサイドメニューのライブラリに「Notion」が追加されます。「Notion」以外の表示がされているときは、それをクリックしてIDを「Notion」に修正してください。
NotionのTokenの登録
以下のコードを実行して、NotionのTokenをGASのプロジェクト上に登録します。{{yourToken}}
を「Notion APIを使う準備」で取得したTokenに書き換えて、setNotionToken
を実行してください。実行後、readProp
を実行して Tokenが表示されることを確認します。
function setNotionToken() {
PropertiesService.getScriptProperties().setProperty("NOTION_TOKEN", "{{yourToken}}")
}
function readProp() {
var props = PropertiesService.getScriptProperties()
console.log(props.getProperty("NOTION_TOKEN"))
}
Tokenが登録され、正しく出力できたら上のコードは削除してOKです。
データベースを解析する
https://www.notion.so/1349f9e927674a03a87d772483dd5b1b?v=05282e02290749fd95decb0df0dc3f5c&p=de3635356a224c569f103a2038dc3521
|--------- DatabaseID ---------| |--------- Page ID ------------|
データベースのレコード(ページ)を開くとPageIDを取得できます。PageIDを使ってデータベースの構造を取得します。
function dispoGetDbPgStructure(client, pgId){
const ret = client.pages.retrieve(pgId)
console.log(JSON.stringify(ret))
}
function testMain() {
var props = PropertiesService.getScriptProperties()
const notion = new Notion.client(props.getProperty("NOTION_TOKEN"))
dispoGetDbPgStructure(notion, "8aa1faecaba847f488f277a4b711a42e")
}
正しくPageIDが設定されていると、JSONでデータベースの行の情報を得られます。Responseのproperties
に各列名があります。列名の配下にはテキストの内容や色、文字の装飾などの情報があります。
単にデータの挿入をするだけであれば、テキストなど必要な情報だけ使うだけで更新できるので、JSONから必要な箇所のみを抜き出します。抜き出した情報を参考にして、TickTickのJSONをNotion用のフォーマットに変換する作業をします。
日付フォーマットの変換
TickTickでは日時情報はJuly 4 2021 at 01:13PM
の形式で、日付情報はJuly 4 2021
で表現しています。Notionではそれぞれ、2021-07-04T13:13:00.000+09:00
、2021-07-04
と表現されます。これにあわせてTickTick→Notionへ日付の書式を変換します。
function _isDatetime(dtString) {
return (dtString.indexOf("AM") >= 0 || dtString.indexOf("PM") >= 0)
}
function _rmDtNoise(dtString) {
var rmAt = dtString.replace(" at ", " ")
var blAM = rmAt.replace("AM", " AM")
var blPM = blAM.replace("PM", " PM")
return blPM
}
function _formatDate(dt, isDt, timezone="+09:00", sep="-") {
var year = dt.getFullYear()
var month = ("00" + (dt.getMonth()+1)).slice(-2)
var day = ("00" + dt.getDate()).slice(-2)
var date = `${year}${sep}${month}${sep}${day}`
if (!isDt) {
return date;
}
var hour = ("00" + (dt.getHours())).slice(-2)
var min = ("00" + (dt.getMinutes())).slice(-2)
var datetime = `${date}T${hour}:${min}:00.000${timezone}`
return datetime
}
function dtFormatter(dtString) {
var fmtDtString = _rmDtNoise(dtString)
var isDT = _isDatetime(fmtDtString)
var dtParse = Date.parse(fmtDtString)
var dt = new Date(dtParse)
return _formatDate(dt, isDT)
}
時間情報を含むか含まないかで処理を分岐させるため、_isDatetime
関数で日時情報を含むか判定します。_rmDtNoise
関数ではat
などを取り除きつつ、時間情報があればAM/PMの前に半角スペースをreplaceを使って挿入します。
Data.parase
で日付の数値情報を取得して、Data
オブジェクトに数値を渡してDate型のオブジェクトをつくります。_formatDate
関数でDate型→String型に変換してNotion用のフォーマットにします。
上記の一連の処理をdtFormatter
関数で実行します。以下はdtFormatter
で日付の書式を変換するコードです。テスト用なので実行後に削除いただいて大丈夫です。
function dispoTest(){
var ret1 = dtFormatter("July 4 2021 at 01:13PM")
console.log(ret1) // 2021-07-04T13:13:00.000+09:00
var ret2 = dtFormatter("July 4 2021")
console.log(ret2) // 2021-07-04
}
Tag(MultiSelect)の変換
NotionのMultiSelect型の構造を単純に表現すると以下の通りになります。
{"Tag": {
"multi_select": [
{"name": "Private",},
{"name": "Work", }
]
}
}
TickTick側のTag情報は#{tagName}
の書式です。複数のタグがある場合には半角スペース区切りでタグを複数表記する書き方となっています。この仕様のため、{tagName}
に#
や半角スペースを含めることができない仕様です。この仕様を使ってmulti_select
の値をつくる関数を書きます。
function genMultiSel(multiSelStr) {
var ret = []
var selList = multiSelStr.split(" ")
selList.forEach((tag) => {
if (tag.length > 0) {
var rmSharp = tag.replace("#", "")
ret.push({name: rmSharp})
}
})
return ret
}
以下はテスト用のコードです。
function test() {
var ret = genMultiSel("#test1 #test2")
console.log(ret)
// [ { name: 'test1' }, { name: 'test2' } ]
}
Body(JSON)の生成
NotionにPOSTするためのJSONをつくります。
function _addDate(params, dateStrings, key, value) {
if (dateStrings.length > 0) {
params["properties"][key] = { "date": { "start": value } }
}
return params
}
function genParams(data, databaseId) {
var params = {
"parent": { "database_id": databaseId },
"properties": {
"TaskName": {
"title": [{ "text": { "content": data.TaskName } }]
},
"TaskContent": {
"rich_text": [{ "text": { "content": data.TaskContent } }]
},
"List": {
"rich_text": [{ "text": { "content": data.List } }]
},
"Priority": {
"rich_text": [{ "text": { "content": data.Priority } }]
},
"LinkToTask": {
"rich_text": [{ "text": { "content": data.LinkToTask } }]
},
"CreatedAt": { "date": { "start": dtFormatter(data.CreatedAt) } }
}
}
var tags = genMultiSel(data.Tag)
if (tags.length > 0) {
params["properties"]["Tag"] = {"multi_select": tags}
}
params = _addDate(params, data.StartDate.length, "StartDate", dtFormatter(data.StartDate))
params = _addDate(params, data.EndDate.length, "EndDate", dtFormatter(data.EndDate))
return params
}
DatabaseIDをつかってデータを流し込む先のデータベースを特定します。データベースの型にあわせてproperties
を設定してあげます。MultiSelectは空文字のケースもあるので、タグの有無にあわせて後から値を追加する処理をしています。これでTickTick→Notionの変換処理は完了です。
doPostをデプロイする
上記の処理をdoPostに反映されます。
function doPost(e) {
// Init
var props = PropertiesService.getScriptProperties();
const notion = new Notion.client(props.getProperty("NOTION_TOKEN"));
var jsonString = e.postData.getDataAsString();
var data = JSON.parse(jsonString);
// Generate a Parameter
const params = genParams(data, "cf0bc59e33734173838e0370c210fa3d")
// Create a record
var ret = notion.pages.create(params)
if (ret["status"] >= 300 && ret["status"] < 200 ) {
const options = {name: "IFTTT Webhooks Error"};
GmailApp.sendEmail("yourAlias@gmail.com", "webhookTest", JSON.stringify(ret), options);
}
}
変更を反映させるためもう一度デプロイする作業をしてください。作業後、新しくURLが発行されるため、そのURLを使ってNotionのデータベースにレコードが追加されるか確認します。エラーメールやエラーメッセージがある場合にはメッセージに従ってデバッグしてください。
IFTTTの設定
最後に、IFTTTからGASのAPIを叩くように設定します。TriggerにはTickTickのNew completed task
を選択します。New completed taskでは対象のListやTag、Priorityを選択できます。Notionのデータベースで集計やフィルターができるので、ここでは以下の通りに設定してすべてのタスクをストックする設定とします。
Item | Value |
---|---|
List | All Lists |
Tag | Please Select(Default) |
Priority | Please Select(Default) |
設定した[Create trigger]をクリックします。Then ThatにはWebhookを選び以下の設定をします。
Item | Value |
---|---|
URL | デプロイしたGAS APIのURL |
Method | POST |
Content Type | application/json |
Body | 以下参照 |
[Body]
{
"TaskName": "{{TaskName}}",
"TaskContent": "{{TaskContent}}",
"CompleteDate": "{{CompleteDate}}",
"StartDate": "{{StartDate}}",
"EndDate": "{{EndDate}}",
"List": "{{List}}",
"Priority": "{{Priority}}",
"Tag": "{{Tag}}",
"LinkToTask": "{{LinkToTask}}",
"CreatedAt": "{{CreatedAt}"
}
設定が完了したら、[Create action]をクリックします。
動作確認
TickTickでタスクをつくり、タスクを完了の状態とします。タスクの完了にあわせてNotionに完了したタスクが登録されたら正常動作となります。なお、IFTTTでイベントが発火するまで数分のラグがあるため、IFTTTの「View activity」やエラーメール、Notionのデータベースなどで動作状況をご確認ください。
まとめ
GASやIFTTTからNotion APIが叩けるようになりました。これにより、タイムトリガーやイベントトリガーでNotionを使えるようになり、より柔軟にデータをストックできるようになります。タスクや習慣・健康管理・分析など幅広く活用できるので、ご自身の用途に合わせて自由に使っていくきっかけになりましたら幸いです。