GoogleAppsScript
Slack
Todoist

SlackからTodoistにタスクを追加する

概要

SlackからTodoistにタスクを追加するbotは公式の提供するものが存在する。
本記事では公式の提供する機能の一つと、Scrapboxでページを作成するオプションを持つbotを実装した。実装した機能を以下に示す。

オプション 詳細
. or タスク タスクの追加を行う
#(プロジェクト名) プロジェクトの下に追加する 未指定ならinbox
!(優先度) 優先度を「(弱)1-4(強)」で設定する 未指定なら1
@(期限) 期限を設定する
-s Scrapboxに同名ページを作成する

下準備

本記事で説明するコードは事前に以下の準備が必要である。本記事での説明は省略する。

実装

「Scrapboxのページを作成する」はScrapboxのリンクを作成することで実現している。そのページが存在しない場合は新規作成されるが、存在する場合はページへのリンクとなる。(参考)
コードを以下に示す。

makeTask.gs
function doPost(e) {
  //Incoming WebHooksの設定で入手したWebHookURL
  var postUrl="https://hooks.slack.com/services/*******";

  var userMessage = (e.parameter.text).replace("タスク","");
  userMessage = (userMessage).replace(".","");

  var status = createTask(userMessage);
  var answerText,answerUrl;

  // botの表示名などを変更する。WebHook設定の方が優先されることもあるため気になる場合は確認する、
  var jsonData = {
     "channel" : "#reminder",
     "username" : "Todoist連携さん",
     "icon_emoji"   :":ghost:",
  };

  if (status[0] === 200) {
      answerText = "Todoistにタスクを追加しました。";
    if(status[1]){
      answerText += "Scrapboxにページを作成します。";
      answerUrl = createPage(status[2]);
      jsonData["attachments"] = [{
        "fallback": answerText+"("+answerUrl+")",
        "color": "#36a64f",
        "pretext": answerText,
        "title": "Scrapbox-"+status[2],
        "title_link": answerUrl,
      }];
    }else{
      jsonData["text"]=answerText;
    }
  } else {
    answerText = "Todoistへのタスク追加に失敗しました。";
    jsonData["text"]=answerText;
  }
  var options =
  {
    "method" : "post",
    "contentType" : "application/json",
    "payload" : JSON.stringify(jsonData)
  };
  UrlFetchApp.fetch(postUrl, options);
}

function createTask(userMessage) {
  var url = "https://todoist.com/API/v7/sync",
      todoistToken = "*******************************",
      uuid = Utilities.getUuid(),
      tempId = Utilities.getUuid(),
      task = strToTask(userMessage),
      projectId = searchProject(task['projectName'],todoistToken),
      formData, response;

  formData = {
    "token": todoistToken,
    "commands": JSON.stringify([{
      "type": "item_add",
      "temp_id": tempId,
      "uuid": uuid,
      "args": {
        "priority" : task['priority'],
        "content": task['taskName'],
        "project_id": projectId, 
      "due": {
        "string": task['limit'],
      },
      }
    }])
  };

  response = UrlFetchApp.fetch(url, {
    "method" : "post",
    "payload" : formData
  });

  return [response.getResponseCode(),task['scrapbox'],task['taskName']];
}

function createPage(taskName){
  //  Scrapboxのページを作成する
  //  ******部分にはScrapboxのプロジェクト名を入力しておく
  var url = "https:///scrapbox.io/*******/"+taskName+"";
  url=encodeURI(url);
  Logger.log(url);
  return url;  
}

function strToTask(str){
  var response = {};
  //  !優先度 で設定
  var priority = str.match(/([!!][1-41-4])/);
  if(priority){
    str = str.replace(priority[0],"");
    response['priority'] = priority[0].slice(1); 
  } else {
    response['priority'] = 1;
  }  

  //  -s でScrapboxのページを作成
  var scrapboxOption = str.match(/(-s)/);
  if(scrapboxOption){
    str = str.replace(scrapboxOption[0],"");
    response['scrapbox'] = true;
  } else {
    response['scrapbox'] = false;
}

  //  #既存のプロジェクト で設定
  var projectName = str.match(/(#|#)[^(@|@)]*/);
  if(projectName){
    str = str.replace(projectName[0],"");
    response['projectName'] = projectName[0].slice(1); 
  }

  //  @期限 で設定
  var limit = str.match(/(@|@).*/);
  if(limit){
    str = str.replace(limit[0],"");
    response['limit'] = limit[0].slice(1);
  }
  response['taskName'] = str;
  return response;
}

function searchProject(projectName,token){
  var url = "https://todoist.com/API/v7/sync",
      formData,options,response;
  var headers = {
      "Accept": "application/json",
  };
  formData ={
    "token": token,
    "sync_token":"*",
    "resource_types":'["projects"]',
  };
  response = UrlFetchApp.fetch(url,{
    "contentType": "application/json",
    "headers":headers,
    "method":"post",
    "payload":JSON.stringify(formData)
  });

  var array = JSON.parse(response.getContentText());
  for(var i = 0; i < array["projects"].length; i++){
    if(array["projects"][i]["name"]===projectName){
      return array["projects"][i]["id"];
    }
  }
  return 000000;
}

実行結果

実際に利用しているbotは上記コードからアイコンとコメントを変更したものである。

Todoist上では以下のように表示される。