Typetalk(タイプトーク)

Backlogの期限切れ課題をTypetalkに自動でお知らせしよう!

Backlogの期限切れ課題をTypetalkに自動でお知らせしよう!

TypetalkのYoshiです。Backlogユーザーの方で「Backlogの課題で期限日を設定したは良いものの、つい放置してしまう」という経験はありませんか?

本記事で紹介するTypetalkボットを使うことで、Backlogの期限切れ課題のリンクをTypetalkに自動で投稿して、課題の担当者にお知らせできます。詳しい使い方をさっそくみていきましょう。

Backlogの期限切れ課題をTypetalkに通知するメリット

このボットを使って、プロジェクトメンバーが参加している対象トピックにBacklogの期限切れの課題を通知することで、メンバー同士で課題の期限切れを認識して、迅速な対応ができます。

最終的なアクションは、プロジェクトマネージャーが決めるかもしれませんが、プロジェクトメンバー同士で進捗の遅れを把握したり意識したりすることで、作業分担やチームのコミュニケーションを円滑にできます

期限切れ課題を通知するTypetalkボットの作成方法

Google Apps Script/Backlog API/Typetalk APIを利用する方法を紹介します。

前提条件

■ Backlogをヌーラボアカウントの組織で使っているTypetalkユーザー

1. Backlogの個人設定ページからAPIキーを発行する

Backlogの課題情報をAPIを使って取得するために、Backlogの個人設定ページからAPIキーを発行します。詳しくはこちらのドキュメントを参考にしてください。

2. Typetalkに通知したいトピック上でボットを作成する

TypetalkにBacklogの期限切れ課題一覧を投稿するために、Typetalkのトピック設定ページからAPIキーを発行します。詳しくはこちらのドキュメントを参考にしてください。

3. Google Apps Scriptにコードを貼り付ける

Google Apps Scriptのサイトにアクセスし、「New Script」をクリックします。ブログの最後に貼ってあるソースコードをコピーして、作成したプロジェクトにソースコードを貼り付けます。貼り付けたら、先の手順で取得したAPIキーなどの設定をソースコード先頭に設定します。設定する変数名は以下のとおりです。

設定したらメニューの「Run」から実行してみましょう。設定が問題なくできていれば、期限切れのタスクをプロジェクトメンバーに教えてくれるようになるはずです!

4.定期実行するタイマーを設定する

実行がうまくいったら、そのコードを定期的に実行するようにしてみましょう。メニュー「Edit」->「Current project’s triggers」からトリガー設定ページを開きます。

「Add Trigger」からトリガーを作成します。毎週月曜日、朝10時に定期実行したい場合は以下のような設定をしてみてください。

通知を毎日送ってしまうと「課題を見直す時間を取らない」「ただ期限日を延長だけしてしまう」という事態が起こり得ます。Typtealkチームとしては、定例ミーティングが行われる日の朝に投稿するなど、適度な通知回数に抑えておくと良いかもしれません。

まとめ

ヌーラボアカウントを使って、Backlogの期限切れ課題をTypetalkに通知する手順をご紹介しました。

プロジェクトの状態をより正確に知るために期限日を設定することは大切です。これを機会にひとりで抱えずにプロジェクトメンバー全員と共有してみましょう。

今回のボットは、Backlogの標準連携としてゆくゆくはTypetalkに組み込む予定です。TypetalkはBacklogに最適なビジネスチャットツールとして、独自の連携機能を強化していきます

もしBacklogをお使いで、Typetalkはまだ試したことがない!という方は、この機会にぜひこちらのページから連携機能を確認してみてくださいね。

ソースコード

var backlogApiKey = '';
var backlogSpaceUrl = 'https://example.backlog.com';
var backlogProjectKey = '' // TYPETALK

var typetalkTopicId = 0;
var typetalkToken = '';

var postMessage = '{project} プロジェクトに期限切れの課題が {count} 件あります。期限切れの課題を確認しましょう:memo:';
var postMaximumLength = 4000;

function main() {
  var project = getProject(backlogProjectKey);
  var expiredIssues = JSON.parse(requestExpiredIssues(project.id));
  if (expiredIssues.length > 0) {
    var messages = buildMessages(project, expiredIssues);
    postToTypetalk(messages);
  }
}

function getProject(projectKey) {
  var response = UrlFetchApp.fetch(backlogSpaceUrl.replace(/\/$/, '') + "/api/v2/projects/" + projectKey + "?apiKey=" + backlogApiKey);
  return JSON.parse(response.getContentText());
}

function dateToyyyyMMdd(date){
  var year = date.getFullYear();
  var month = ("00" + (date.getMonth() + 1)).slice(-2);
  var day = ("00" + date.getDate()).slice(-2);
  return year + "-" + month + "-" + day;
}

function requestExpiredIssues(projectId) {
  var today = new Date();
  var yesterday = new Date();
  yesterday.setDate(today.getDate() - 1);

  var dueDateUntil = dateToyyyyMMdd(yesterday);
  var response = UrlFetchApp.fetch(backlogSpaceUrl.replace(/\/$/, '') + "/api/v2/issues?projectId[]=" + projectId + "&dueDateUntil=" + dueDateUntil + "&statusId[]=1&statusId[]=2&statusId[]=3&sort=dueDate&count=100&order=asc&apiKey=" + backlogApiKey);
  return response.getContentText();
}

function buildMessages(project, expiredIssues) {
  var messages = [];
  var message = postMessage.replace("{project}", project.name).replace("{count}", expiredIssues.length);

  expiredIssues.forEach(function(expiredIssue) {
    var mention = '';
    var assignee = expiredIssue.assignee;
    if (assignee != null) {
      var nulabAccount = assignee.nulabAccount;
      if (nulabAccount != null) {
        mention = '@' + nulabAccount.uniqueId;
      }
    }

    var newMessage = expiredIssue.issueKey + " " + expiredIssue.summary + " " + mention;
    if (message.length + 1 + newMessage.length > postMaximumLength) { // Split message to support limited message length
      messages.push(message);
      message = newMessage;
    } else {
      message += "\n" + newMessage;
    }
  });

  messages.push(message);
  return messages;
}

function postToTypetalk(messages) {
  var replyTo = null;
  messages.forEach(function(message) {
    var formData = {
      'message': message,
      'replyTo': replyTo
    };

    var options = {
      'method' : 'post',
      'payload' : formData
    };

    var response = UrlFetchApp.fetch("https://typetalk.com/api/v1/topics/" + typetalkTopicId + "?typetalkToken=" + typetalkToken, options);
    var json = JSON.parse(response.getContentText());
    replyTo = json.post.id;
  });
}

BacklogとTypetalkの連携機能はこちら

TypetalkはBacklogの公式チャットツールとして、Backlogの課題管理を円滑にするオリジナル機能が盛りだくさんです。この機会にぜひTypetalkを試してみませんか?

申し込み手順などの詳しい情報は連携機能の紹介ページからぜひご確認ください。

▼TypetalkとBacklogの連携機能に関連する記事はこちら