3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Todoistの完了タスクをAPIとGoogle Apps Scriptを経由してSpreadsheetに記録する

Last updated at Posted at 2020-10-24

目的

Todoistの完了タスクをログに残したい。有料プランに切り替えれば済む話なのかもしれないが、それだけのために課金するのもどうか。
ということで他の方法で記録していたのですが、今まではIFTTTで簡単に実現していました。IFTTTの有料化に伴い、別の処理に置き換えます。

概要

TodoistのWebhooksにより、指定したURLに完了タスクデータをPOSTします。
あらかじめGoogle Apps ScriptのPOST処理を作成し、このURLを使用します。
処理の中でSpreadsheetに記録します。このときヘッダ行を見て記録する列を認識します。

処理の流れとは逆の順に準備を進めます。(多少行って戻ってしますが)

  1. 記録先のSpreadsheetを作成する
  2. 記録処理のGoogle Apps Scriptを用意する
  3. WebhookのURLを設定する

手順

記録先のSpreadsheetを作成する

  • 任意の場所、名前でSpreadsheetを作成する
  • このとき1行目を下の画像のように設定する(スクリプトの処理を理解した上で変更も可能)
  • URLを後で使う

screenshot1.png

記録処理のGoogle Apps Scriptを用意する

  • 任意の場所、名前でGoogle Apps Scriptを作成する
    作成する方法、準備については他の記事を参照
    https://qiita.com/tags/googleappsscript
  • スクリプトを下の内容に書き換え
var SPREADSHEET_ID = "********"; // ここにSpreadsheetのURLを
var TODOIST_API_TOKEN = "********"; // 後述

// Compiled using ts2gas 3.6.3 (TypeScript 3.9.7)
var Todoist = /** @class */ (function () {
    function Todoist() {
    }
    Todoist.prototype.read = function () {
        var _this = this;
        if (typeof this.labels !== "undefined" || typeof this.projects !== "undefined") {
            return;
        }
        var url = "https://api.todoist.com/sync/v8/sync"
            + ("?token=" + TODOIST_API_TOKEN)
            + "&sync_token=*"
            + '&resource_types=[%22labels%22,%22projects%22]';
        var response = UrlFetchApp.fetch(url);
        var data = JSON.parse(response.getContentText("UTF-8"));
        this.labels = {};
        if (Array.isArray(data.labels)) {
            data.labels.forEach(function (label) { return _this.labels[label.id] = label.name; });
        }
        this.projects = {};
        if (Array.isArray(data.projects)) {
            data.projects.forEach(function (project) { return _this.projects[project.id] = project.name; });
        }
    };
    Todoist.prototype.labelName = function (id) {
        this.read();
        return this.labels[id];
    };
    Todoist.prototype.labelNames = function (ids) {
        var _this = this;
        var names = [];
        ids.forEach(function (id) { return names.push(_this.labelName(id)); });
        return names.join(",");
    };
    Todoist.prototype.projectName = function (id) {
        this.read();
        return this.projects[id];
    };
    return Todoist;
}());
var MySpreadsheet = /** @class */ (function () {
    function MySpreadsheet(id, sheetId) {
        if (sheetId === void 0) { sheetId = 0; }
        if (id.match(/^https:\/\//)) {
            this.ss = SpreadsheetApp.openByUrl(id);
        }
        else {
            this.ss = SpreadsheetApp.openById(id);
        }
        this.sheet = this.ss.getSheets()[sheetId];
    }
    MySpreadsheet.prototype.headers = function () {
        var range = this.sheet.getRange(1, 1, 1, this.sheet.getMaxColumns());
        return range.getValues()[0];
    };
    MySpreadsheet.prototype.appendRow = function (row) {
        var insertRow = [];
        this.headers().forEach(function (name, index) {
            insertRow.push((name && name in row) ? row[name] : "");
        });
        this.ss.appendRow(insertRow);
    };
    MySpreadsheet.prototype.unshiftRow = function (row) {
        this.sheet.insertRowAfter(1);
        var range = this.sheet.getRange(2, 1, 1, this.sheet.getMaxColumns());
        this.headers().forEach(function (name, index) {
            if (name) {
                range.getCell(1, index + 1).setValue(row[name]);
            }
        });
    };
    return MySpreadsheet;
}());
var RESPONSE_STATUS;
(function (RESPONSE_STATUS) {
    RESPONSE_STATUS["SUCCESS"] = "success";
    RESPONSE_STATUS["ERROR"] = "error";
})(RESPONSE_STATUS || (RESPONSE_STATUS = {}));
var MyResponse = /** @class */ (function () {
    function MyResponse(status, message) {
        this.status = status;
        this.message = message;
    }
    MyResponse.prototype.toJSON = function () {
        return {
            status: this.status,
            message: this.message
        };
    };
    MyResponse.prototype.create = function () {
        var output = ContentService.createTextOutput();
        output.setMimeType(ContentService.MimeType.JSON);
        output.setContent(JSON.stringify(this));
        return output;
    };
    return MyResponse;
}());
function doPost(e) {
    var data = JSON.parse(e.postData.contents);
    var response = new MyResponse(RESPONSE_STATUS.SUCCESS, "ok");
    try {
        var todoist = new Todoist();
        var row = {
            content: data.event_data.content,
            project: todoist.projectName(data.event_data.project_id),
            due: "",
            completed_at: Utilities.formatDate(new Date(data.event_data.date_completed), Session.getScriptTimeZone(), "yyyy-MM-dd HH:mm:ss"),
            labels: "",
            priority: data.event_data.priority,
            url: data.event_data.url
        };
        if (data.event_data.due !== null) {
            row.due = data.event_data.due.date;
        }
        if (data.event_data.labels.length > 0) {
            row.labels = todoist.labelNames(data.event_data.labels);
        }
        var ss = new MySpreadsheet(SPREADSHEET_ID);
        ss.appendRow(row);
    }
    catch (err) {
        console.error(err);
        console.log(data);
        response.status = RESPONSE_STATUS.ERROR;
        response.message = err.message;
    }
    return response.create();
}

コンパイル前のコードはこちら。修正する場合は参考にしてください。
https://github.com/kunikada/todoist-logs-completed

WebhookのURLを設定する準備

Google Apps Scriptを公開する

  • さきほどのスクリプトを開く
  • Test tokenをコード中のTODOIST_API_TOKENに設定
  • コードを保存する
  • メニュー > 公開 > ウェブアプリケーションとして導入...
  • URLを後で使う

WebhookのURLを設定する

  • Todoistの管理コンソールを開く
  • 作成したAppを開く
  • Webhooks callback URLにさきほどのURLを設定
  • Watched Eventsitem:completedにチェック
  • Save webhooks configuration

おわり

お疲れさまでした。

参考

Todoist API 公式ドキュメント
https://developer.todoist.com/

Google Apps Script リファレンス
https://developers.google.com/apps-script/reference

3
3
0

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
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?