18
18

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 5 years have passed since last update.

LINE BOTからTodoistのタスク作成

Last updated at Posted at 2017-01-29

妻からの買い物リストの管理をTodoistでやっています。
LINEのMessaging APIを使ってみたかったのと、もっと簡単にタスク作成をしたかったので、Todoistのタスク作成をするLINE BOTを作ってみました。

LINE BOTの準備

準備するもの

  • LINE Businessアカウント
  • HTTPSに対応したサーバ

LINE Businessアカウント

 * 以下から利用登録
https://business.line.me/ja/services/bot

Messaging APIかDeveloper Trialかお好きな方を。

Messaging APIとDeveloper Trialの違い

Developer Trial Messaging API(Free Plan)
Push API(※1) 利用できる 利用できない
追加可能友だち数 50人まで 制限なし
プレミアムID 利用できない 利用可能
API Rate limits 1,000/分 10,000/分
  • (※1)Push API
    Push APIはキャンペーン通知などBOTから任意のイベントでメッセージを送る

LINE BOTの設定

LINE@ MANAGERから設定します。

  • Webhook送信: 利用する
    利用しないとサーバにリメッセージが送られない

  • Botのグループトーク参加: どちらでも好きな方を

  • 自動応答メッセージ: 利用しない
    利用するにすると固定メッセージしか返さないので注意

HTTPSに対応したサーバ

HerokuやAWSをつかった記事がよくありますけど、今回はGoogle Apps Script(GAS)で構築します。

GASの実装

  • GASの作成
    その他から表示されなかったら「アプリを追加」から作成
スクリーンショット 2017-01-29 21.56.51.png

LINEからのリクエストを受け取る

まずはオウム返しするサンプルを作成します

Messaging APIはPOSTでリクエストしてくるのでdoPost関数を実装します

コード.gs
var CHANNEL_ACCESS_TOKEN = 'LINE Developersから取得'

function doPost(e) {
  // 返事用のトークン。一度のみ使うことができる
  var replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
  // 投稿したメッセージ
  var userMessage = JSON.parse(e.postData.contents).events[0].message.text;

  var url = 'https://api.line.me/v2/bot/message/reply';

  UrlFetchApp.fetch(url, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': replyToken,
      'messages': [{
        'type': 'text',
        'text': userMessage, // オウム返しをする
      }],
    }),
  });
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}

ウェブアプリケーションとして導入

Messaging APIからGASへのリクエストを行うためにGASを公開してURLをLINE側に登録します。

  • GASから導入 > ウェブアプリケーションとして導入で公開
  • URLをコピってLINE DevelopersのWebhook URLに設定

 スクリーンショット 2017-01-29 21.56.51.png

あとは作成したアカウントを友達登録してメッセージを送信するとオウム返ししてくれます。

Todoistのタスク登録

続いて本題のTodoistのタスク登録を実装します。
TodoistのAPI: https://developer.todoist.com/

まずはTodoistのAPIをたたくためのAPIトークンを取得

  • Todoistの設定 > アカウント > APIトークン
     スクリーンショット 2017-01-29 21.56.51.png

そして取得したAPIトークンでTodoistのAPIにリクエスト

コード.gs
var CHANNEL_ACCESS_TOKEN = 'LINE Developersから取得'

function doPost(e) {
  // 返事用のトークン。一度のみ使うことができる
  var replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
  // 投稿したメッセージ
  var userMessage = JSON.parse(e.postData.contents).events[0].message.text;

  var status = createTask(userMessage);
    
  if (status === 200) {
    answerText = userMessage + "を作成した!";
  } else {
    answerText = "何か失敗した。。。";
  }

  var url = 'https://api.line.me/v2/bot/message/reply';

  UrlFetchApp.fetch(url, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': replyToken,
      'messages': [{
        'type': 'text',
        'text': answerText
      }],
    }),
  });
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}

// タスク登録
function createTask(userMessage) {
  var url = "https://todoist.com/API/v7/sync",
      uuid = Utilities.getUuid(),
      tempId = Utilities.getUuid(),
      formData, response;
  
  formData = {
    "token": "TODOIST API TOKEN",
    "commands": JSON.stringify([{
      "type": "item_add",
      "temp_id": tempId,
      "uuid": uuid,
      "args": {
        "content": userMessage[0],
        "project_id": 000000 // TodoistのプロジェクトのID
      }
    }])
  };

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

これで、何か登録したいタスクをメッセージに投稿すると、Todoistに登録される
スクリーンショット 2017-01-29 21.56.51.png

Todoistのタスク一覧取得

次に、登録したタスク一覧をTemplate messageのCarouselを使って返すようにしました

Carouselはこんな感じのやつ
スクリーンショット 2017-01-29 21.56.51.png

コード

注意点として、

  • Template messageのCarouselは5件までしか横にスライドできないので、columnの配列の件数を5件にする必要がある
  • 5件以上ある場合は複数メッセージ(messages)を返す必要がある
コード.gs
var CHANNEL_ACCESS_TOKEN = 'LINE Developersから取得'

function doPost(e) {
  // 返事用のトークン。一度のみ使うことができる
  var replyToken = JSON.parse(e.postData.contents).events[0].replyToken;
  // 投稿したメッセージ
  var userMessage = JSON.parse(e.postData.contents).events[0].message.text;

  // > listだとタスク登録しない
  if (userMessage.indexOf("> list") === 0) {
      var response = getTasks();
      messages = createCarousel(response.items));
  } else {
    var status = createTask(userMessage);
    
    if (status === 200) {
      answerText = userMessage + "を作成した!";
    } else {
      answerText = "何か失敗した。。。";
    }
    messages = [{
      'type': 'text',
      'text': answerText
    }];
  }

  var url = 'https://api.line.me/v2/bot/message/reply';

  UrlFetchApp.fetch(url, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': replyToken,
      'messages': messages,
    }),
  });
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}

// タスク一覧の取得
function getTasks() {
  var url = "https://todoist.com/API/v7/projects/get_data",
      formData, options, response;
  
  formData = {
    "token": "TODOIST API TOKEN",
    "project_id": "Todoist Project ID"
  };

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

// Carousel情報の作成
function createCarousel(items) {
  var i, max,
      columns = [],
      itemsArray = [],
      messages = [];
      
  // Template messageのCarouselは5件までしか横にスライドできないので、複数メッセージを返す必要がある
  for (i = 0; i < items.length; i += 5) {
    itemsArray.push(items.slice(i, i + size));
  }
  // columnsに5件ずつの複数のメッセージを作成
  for (i = 0, max = itemsArray.length; i < max; i++) {
    messages.push( {
      type: "template",
      altText: "一覧",
      template: {
        type: "carousel",
        columns: createColumns(itemsArray[i])
      }
    } );
  }
  
  return messages;
}

function createColumns(items) {
  var i, max,
      columns = [];
      
  for (i = 0, max = items.length; i < max && i < 5; i++) {
    columns.push({
      text: items[i].content,
      actions: [{            // ボタンの設定
        "type": "message",
        "label": "完了",
        "text": "> finish " + items[i].id
      }]
    });
  }
  return columns;
}

これで"> list"と投稿すると一覧を返します

スクリーンショット 2017-01-29 21.56.51.png

リッチメニューの作成

LINEの公式アカウントを見ているとトークを開いた途端に下から出てくるやつです。(ヤマト運輸とか)
> listと打つのはめんどいのでリッチメニューからタスク一覧をすぐに表示できるようにします。

LINE@ MANAGER > リッチコンテンツ作成 > リッチメニューから作成します。

スクリーンショット 2017-01-29 21.56.51.png

おまけ

Google Apps Scriptのコード内からセキュアな文字列を使いたいときスクリプトプロパティを使うといい感じです。

スクリーンショット 2017-01-29 21.56.51.png
コード.gs
var property = PropertiesService.getScriptProperties().getProperty("CHANNEL_ACCESS_TOKEN");
18
18
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
18
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?