ChatworkのタスクをGoogleカレンダーに同期するスクリプトを書いた。

タスクの管理難民のみなさん、こんにちは。

タスクの管理を楽にしたいなと思って、Trelloなどのツールを試してみたものの、「タスクを管理する」という新たなタスクが生まれてしまい、本末転倒なことになったりしませんか?

なりませんか、そうですか。

僕はクライアントさんとのやり取りは、主にChatworkを使っているのですが、タスク管理もChatworkで行っています。新たなタスク管理アプリケーションを起動する手間もなく、毎日必ず使うChatworkで完結するところがシンプルで良いなと思っています。

ChatworkのタスクをGoogleカレンダーでも確認できたら、便利だよなと思いまして、連携スクリプトをGASで書いてみました。

GASを利用して、Chatwork APIからタスクデータを取得し、Googleカレンダーに転記する

GASとは、「Google Apps Script」のことで、Googleアカウントをお持ちの方なら誰でも無料で使用できる自動化のためのウェブサービスです。

GASの文法は、ほぼJavaScriptそのもので、JavaScriptにGAS専用の命令を追加したものになっています。新たな言語を習得する必要がないところが良いですよね。

以下の手順で導入します。

1. Googleカレンダーに専用のカレンダーを追加し、カレンダーIDを取得する。

既存のカレンダーでも使用できるようにスクリプトを組んでいますが、誤って他の予定が削除されないよう、念のために新規カレンダーを追加して使用するようにしてください。

  1. パソコンのブラウザでGoogleカレンダーを表示する。
  2. 左下の「他のカレンダー」の右にある「+」をクリックする。
  3. メニューが表示されるので、「新しいカレンダーを追加」をクリックする。
  4. カレンダーに名前(chatworkなど任意の名称でOK)を付けて、カレンダーを追加する。
  5. マイカレンダーに追加されるので、カレンダー名の右にある「︙」をクリックする。
  6. メニューが表示されるので、「設定と共有」をクリックする。
  7. 「カレンダーの統合」という項目の欄に「カレンダー ID」が表示されるので、このIDをメモしておく。

2. Chatworkにログインして、APIトークンを取得する。

  1. 右上の▼をクリックすると、メニューが表示されるので、「サービス連携」をクリックする。
  2. 左メニューの「API Token」をクリックすると、APIトークンが表示されるので、メモしておく。

3. GASにスクリプトを登録する。

  1. Googleにログインして、Googleドライブを表示する。
  2. 「+新規」ボタンをクリックし、「Google Apps Script」を選択する。
  3. コード編集エリアに、以下のスクリプトをコピペする。
  4. 「CALENDAR_ID」と「CHATWORK_TOKEN」を先程メモしたものに書き換える。
  5. [ファイル]→[保存]でスクリプトに名前(任意の名称)を付けて保存する。
// googleカレンダーID
const CALENDAR_ID = '**** ここにGoogleカレンダーIDを記入する *****'

// Charwork API token
const CHATWORK_TOKEN = '***** ここにChatwork APIトークンを記入する *****'

// タイトルの最大文字数
const MAX_TITLE_LENGTH = 50

// 対象月(取得できるタスクの上限が100件なので、期間を長く設定しすぎると取りこぼす)
// 何ヶ月前まで同期するか
const MONTH_FROM = 2
// 何ヶ月先まで同期するか
const MONTH_TO = 6

// Chatwork API End Point
const CHATWORK_API_URL = 'https://api.chatwork.com/v2/my/tasks?status=open'

// カレンダーイベントに付与するタグのキー
const CALENDAR_TAG_KEY = 'chatwork'
// トリガーからこの関数をコールする!

function syncChatwork() {
  // googleカレンダーオブジェクトを生成する
  const calendar = CalendarApp.getCalendarById(CALENDAR_ID)

  // 対象期間を設定する
  const term = getTerm()

  // カレンダーの予定をクリアする
  clearEvents(calendar, term)

  // Chatworkのタスクデータを取得する
  const tasks = getTasks()

  // 件数
  let count = 0

  // タスクをカレンダーに登録する
  tasks.forEach((task) => {
    // タスクの期日を取得する
    const date = getDate(task, term)

    // 日付が空の場合、処理を抜ける
    if (!date) {
      return
    }

    // タイトルを設定する
    const title = getTitle(task)

    // タスクの概要を設定する
    const description = getDescription(task)

    // カレンダーにイベントを登録する
    const event = calendar.createAllDayEvent(title, date, { description })
    event.setTag(CALENDAR_TAG_KEY, '1')

    // 件数をカウントする
    count += 1
  })
  // ログを出力する
  Logger.log(`未完了タスク数: ${count}`)
}

// 対象期間を返す
const getTerm = () => {
  const dateFrom = new Date()
  const dateTo = new Date()
  dateFrom.setMonth(dateFrom.getMonth() - MONTH_FROM)
  dateTo.setMonth(dateTo.getMonth() + MONTH_TO)

  return {
    from: dateFrom,
    to: dateTo,
  }
}

// カレンダーの予定を削除する関数
const clearEvents = (calendar, term) => {
  // 過去の予定は1日多く削除する(残ってしまうため)
  const dateFrom = new Date(term.from.getTime() - 24 * 60 * 60 * 1000)

  // 指定期間の全予定を取得する
  const events = calendar.getEvents(dateFrom, term.to)

  // 該当する予定を削除する
  events.forEach((event) => {
    if (!event.getTag(CALENDAR_TAG_KEY)) {
      return
    }
    event.deleteEvent()
  })
}

// Chatworkからタスクを取得する関数
const getTasks = (term) => {
  // チャットワークAPIエンドポイントからデータを取得する
  const response = UrlFetchApp.fetch(CHATWORK_API_URL, {
    headers: { 'X-ChatWorkToken': CHATWORK_TOKEN },
    method: 'get',
  })

  // タスクデータをパースして返却する
  return !response ? [] : JSON.parse(response.getContentText())
}

// タスクの期日を返す関数
const getDate = (task, term) => {
  // 期限が設定されていないタスクは無視する
  if (!task.limit_time) {
    return null
  }

  // UNIXTIMEをDATEオブジェクトに変換する
  const date = new Date(task.limit_time * 1000)

  // 対象期間外のタスクは無視する
  if (date.getTime() < term.from.getTime() || term.to.getTime() < date.getTime()) {
    return null
  }

  return date
}

// タスクのタイトルを返す関数
const getTitle = (task) => {
  const title = task.body.split(String.fromCharCode(10)).join(' ') // 改行をスペースに置換
    .replace(/\[/g, '<') // [ → < に置換
    .replace(/\]/g, '>') // ] → > に置換
    .replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,'') // HTMLタグを除去

  return title.length > MAX_TITLE_LENGTH ? `${title.substring(0, MAX_TITLE_LENGTH)}...` : title
}

// タスクの詳細を返す関数
const getDescription = (task) => {
  return `【${task.room.name}】by ${task.assigned_by_account.name}\n${task.body}`
}

4. 動作のテストをする。

  1. [実行]→[関数を実行]→[syncChatwork]をクリックする。
  2. 初回の実行時に、認証画面が出るので「許可」をクリックする。
  3. 処理が正常に完了すると、GoogleカレンダーにChatworkのタスクが追加される。

5. スクリプトが繰り返し自動的に呼び出されるようトリガーを設定する。

  1. [編集]→[現在のプロジェクトのトリガー]をクリックする。
  2. 右下の「+ トリガーを追加」をクリックする。
  3. 以下のように設定して保存する。
実行する関数を選択syncChatwork
実行するデプロイを選択Head
イベントのソースを選択時間主導型
時間ベースのトリガーのタイプを選択時間ベースのタイマー
時間の間隔を選択(時間)1時間おき

ここでセットしたトリガーのタイミング(上記の例では、1時間に1回)で、スクリプトが呼び出されて、タスクの内容がGoogleカレンダーに転記されます。(なので、即座に反映するわけでない)

期限が設定されてないタスクや完了済みのタスクは、カレンダーに同期しないようになっています。

Googleカレンダーに書き込める回数に上限(1日に1000回までらしい)があります。このスクリプトは、カレンダーに登録されているタスクを一旦すべて削除して、Chatworkから取得したタスクを新たに登録する仕様になっています。なので、割とすぐに上限に達してしまいます。

ちなみに、上記のスクリプトでは、過去2ヶ月から6か月先までのタスクを、1時間に1回同期します。例えば対象となるタスクが10個あると、1日に 10 x 24 = 240 件のタスクがカレンダーに書き込まれることになります。

タスク数が多い人や、もっと頻繁に同期したい方は、差分だけを抽出して最小限の削除・更新・追加を行うようにスクリプトを書き換えればスマートにクリアできると思います。(差分の抽出は結構面倒…)

まとめ

GASを使うと、Googleのサービスと他のサービスを連携して使うことが出来るようになります。JavaScriptを書ける人ならば、他にもいろいろな活用ができると思いますので、是非チャレンジしてみてください。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする