BLOG【GAS】TrelloからSlackへ定期的に通知を飛ばしてみた

みなこ
  1. HOME
  2. ブログ
  3. フロントエンド
  4. 【GAS】TrelloからSlackへ定期的に通知を飛ばしてみた

目次

  1. ✏️ 概要
  2. 💼 準備をする
    1. Trelloのキーとトークンを取得する
    2. Googleスプレッドシートを用意する
    3. 通知を投稿するチャンネルを用意する
    4. GASを書く準備をする
  3. 📅 コードを書いてみる
    1. 完成版コード
    2. コードの解説
  4. 🎉 実際にコードを動かしてみよう

✏️ 概要

Trelloカレンダーシリーズも第三弾となりました(笑)Trelloのカレンダー化から始まりカレンダーリストの自動生成を行い 思いの外使い心地が良くかなり愛用してた挙句に今度はslackにリマインダーを飛ばしたくなったので書いてみました!

過去の記事はこちら ↓

https://doctype.jp/blog/frontend/80/

【Chrome拡張機能】Trelloをカレンダー化する拡張機能を作ってみた

https://doctype.jp/blog/backend/507/

【Trello API, PHP】TrelloのAPIを使ってカレンダーを作ってみた

今回はTrelloカレンダーの中から特定の日付のリストに存在するチケットをSlackに通知します。9amと7pm にその日のリスト、11pmに次の日のリストにあるチケットをslackに飛ばします。

💼 準備をする

Trelloのキーとトークンを取得する

Trelloの開発者向けAPIを使用するにあたりキーとトークンを取得します。こちらに関しては以前に記事にしているので こちら(https://doctype.jp/blog/backend/507/)の 開発者向けAPIキーの取得トークンの取得 を参考にキーとトークンの取得を行ってください!

また、 APIを実際に叩いてみる を実行し今回通知をしたいボードのIDを取得しメモを取っておいてください!

Googleスプレッドシートを用意する

slackに飛ばすチケット情報の置き場としてスプレッドシートを用意します。任意のスプレッドシートを作成し「target」という名前のシートを用意してください。

用意ができたらスプレッドシートのIDをメモします。スプレッドシートのURLの /d/ と /edit の間にあるのがIDになります

https://docs.google.com/spreadsheets/d/{ここの部分がID}/edit#gid=12345

通知を投稿するチャンネルを用意する

任意のSlackに通知を飛ばす先のチャンネルを用意し そのチャンネルのWebhook URLを取得しましょう!以下の記事を参考にさせていただきましたありがとうございます 🙏

https://qiita.com/vmmhypervisor/items/18c99624a84df8b31008

引用元:SlackのWebhook URL取得手順

Webhook URLが取得できたらメモしておきましょう!

GASを書く準備をする

さっき作ったスプレッドシートの ツール から スクリプトエディタ を選択しエディタへ移動します。次に、先ほど用意したキーやトークンなど諸々の情報を設定していきます。スクリプトエディタの ファイル から プロジェクトのプロパティ を選択し取得したそれぞれの情報を設定していきましょう(名前が雑ですすみません……

  • bord => 対象のTrelloボードのID
  • sheet_id => スプレッドシートのID
  • slack => 投稿するチャンネルのWebhookURL
  • key => Trelloのキー
  • token => Trelloのトークン

📅 コードを書いてみる

準備お疲れ様でした….. それではいざ!!!コードを書いていきましょう。

まずは完成版のコードを見てみましょう!解説は後述します。

以下のコードをスクリプトエディタに記載します。

完成版コード

個人用に書いてたコードなのでかなり変数名が雑ですごめんなさい

/*
    9時, 19時にその日の予定
    23時に次の日の予定
    を slackに投稿する
 */

function setTrigger() {
    let setTime = new Date();
    const current_time = setTime.getHours();

    if (0 <= current_time && current_time < 9) {
        // 基本的にここの処理は通らないはず
        setTime.setHours(9);
        setTime.setMinutes(00);
    } else if (9 <= current_time && current_time < 19) {
        setTime.setHours(19);
        setTime.setMinutes(00);
    } else if (19 <= current_time && current_time < 23) {
        setTime.setHours(23);
        setTime.setMinutes(00);
    } else {
        setTime.setDate(setTime.getDate() + 1)
        setTime.setHours(9);
        setTime.setMinutes(00);
    }

    ScriptApp.newTrigger('main').timeBased().at(setTime).create();
}

function main() {
    setTrigger();

    let post_card_num = 0
    const prop = PropertiesService.getScriptProperties().getProperties();

    // 固定値
    const key   = prop.key;  // SlackAPIキー
    const token = prop.token; // Slackトークン
    const boardid = prop.bord; // TrelloのボードID
    const sheet = SpreadsheetApp.openById(prop.sheet_id); // 出力するスプレットシートのID

    // 前回の読み込みをクリア
    sheet.getSheetByName('target').clear();

    // リスト情報の取得
    const listurl = "https://trello.com/1/boards/" + boardid + "/lists?key=" + key + "&token=" + token + "&fields=name";
    const listres = UrlFetchApp.fetch(listurl);
    const listjson = JSON.parse(listres.getContentText());

    // カード情報の取得
    const apiurl = "https://trello.com/1/boards/"+ boardid +"/cards?key="+ key +"&token="+ token;
    const cardres = UrlFetchApp.fetch(apiurl);
    const cardjson = JSON.parse(cardres.getContentText());

    // Dateオブジェクトの作成
    const today_obj = new Date();

    // 対象の日付を出し分ける用に現在の時間を取得
    const current_time = today_obj.getHours();

    // 今日の日付を取得
    const today_month = ("0"+(today_obj.getMonth() + 1)).slice(-2);
    const today_date = ("0"+today_obj.getDate()).slice(-2);

    // 明日の日付を取得
    today_obj.setDate( today_obj.getDate() + 1 );
    const next_day_month = ("0"+(today_obj.getMonth() + 1)).slice(-2);
    const next_day_date = ("0"+today_obj.getDate()).slice(-2);

    // 対象の日付を検索する時に使用する文字列
    let search_target_string

    // postする時に使用する文字列
    let post_string
    let post_date_string

    if (0 <= current_time && current_time < 23) {
        search_target_string = today_month + '/' + today_date
        post_string = '今日'
    } else {
        search_target_string = next_day_month + '/' + next_day_date
        post_string = '明日'
    }

    // 対象のリストのidを格納する変数
    let target_id

    // slackにpostする対象の日付と一致したリストのIDを取得
    for (i = 0; i < listjson.length; i++) {
        if (listjson[i]['name'].match(search_target_string)) {
            target_id = listjson[i]['id']
            post_date_string = listjson[i]['name']
        }
    }

    // slackにpostする対象の日付のリストにあるカードを「target」シートに出力
    for (i = 0; i < cardjson.length; i++) {
        if (cardjson[i]['idList'].match(target_id)) {
            sheet.getSheetByName('target').getRange(post_card_num+2,3).setValue(cardjson[i]['name']); // カード名をシートに記載
            sheet.getSheetByName('target').getRange(post_card_num+2,4).setValue(cardjson[i]['shortUrl']); // カードURLをシートに記載

            post_card_num += 1
        }
    }

    let post_data = ['<!channel>'];

    //メッセージをSlackに送る
    if(post_card_num > 0) {
        post_data.push(post_string + '' + post_date_string + 'の予定だよ!!');
        for (i = 0; i < post_card_num; i++) {
            let cardname = sheet.getSheetByName('target').getRange(i+2,3).getValue(); // カード名
            let cardurl = sheet.getSheetByName('target').getRange(i+2,4).getValue(); // カードURL

            post_data.push('=================\n- カード名:' + cardname +'\n- カードURL:'+ cardurl);
        }
    } else {
        post_data.push(post_string + ' ' + post_date_string + 'の予定はありません!!');
    }

    const sentence = post_data.join('\r\n');
    const payload = {'text' : sentence,};
    const options = {
        'method' : 'post' ,
        'contentType' : 'application/json' ,
        'payload' : JSON.stringify(payload),
        'link_names' : 1,
    };

    // 投稿するwebhookURL
    const url = prop.slack;

    UrlFetchApp.fetch(url, options);
}

コードの解説

固定値の設定

まずはmain関数からです。

    let post_card_num = 0
    const prop = PropertiesService.getScriptProperties().getProperties();

    // 固定値
    const key   = prop.key;  // SlackAPIキー
    const token = prop.token; // Slackトークン
    const boardid = prop.bord; // TrelloのボードID
    const sheet = SpreadsheetApp.openById(prop.sheet_id); // 出力するスプレットシートのID

    // 前回の読み込みをクリア
    sheet.getSheetByName('target').clear();

先ほどプロジェクトのプロパティで設定した固定値をコード内で使えるように呼び出します。GASのスクリプトエディタでは 、PropertiesService の getScriptProperties().getProperties() で、設定したプロジェクトのプロパティを呼び出せます ✔️

また、Slackに通知するカードを記録しているシートから前回の読み込みをクリアします 🧹

Trelloからリストとカードを取得

    // リスト情報の取得
    const listurl = "https://trello.com/1/boards/" + boardid + "/lists?key=" + key + "&token=" + token + "&fields=name";
    const listres = UrlFetchApp.fetch(listurl);
    const listjson = JSON.parse(listres.getContentText());

    // カード情報の取得
    const apiurl = "https://trello.com/1/boards/"+ boardid +"/cards?key="+ key +"&token="+ token;
    const cardres = UrlFetchApp.fetch(apiurl);
    const cardjson = JSON.parse(cardres.getContentText());

続いてTrelloAPIを使用し対象のボードからリストとカードの情報を取得し変数に格納しておきます 💡 この後の処理で変数に格納された情報の中からSlackに通知したい情報を絞り込みます!

対象の日付リストを探すための用意する

今回は通知する時間によってSlackに通知するカードの対象の日付を変えたいので、その日と次の日の情報を生成しておきます。10:59pmまでの通知はその日のリストから 11pm以降の通知は次の日のリストからカードを取得しSlackに通知します。


    // Dateオブジェクトの作成
    const today_obj = new Date();

    // 対象の日付を出し分ける用に現在の時間を取得
    const current_time = today_obj.getHours();

    // 今日の日付を取得
    const today_month = ("0"+(today_obj.getMonth() + 1)).slice(-2);
    const today_date = ("0"+today_obj.getDate()).slice(-2);

    // 明日の日付を取得
    today_obj.setDate( today_obj.getDate() + 1 );
    const next_day_month = ("0"+(today_obj.getMonth() + 1)).slice(-2);
    const next_day_date = ("0"+today_obj.getDate()).slice(-2);

    // 対象の日付を検索する時に使用する文字列
    let search_target_string

    // postする時に使用する文字列
    let post_string
    let post_date_string

    if (0 <= current_time && current_time < 23) {
        search_target_string = today_month + '/' + today_date
        post_string = '今日'
    } else {
        search_target_string = next_day_month + '/' + next_day_date
        post_string = '明日'
    }

ちなみに (“0″+(today_obj.getMonth() + 1)).slice(-2); みたいなことをしているのは、日付リストをPHPのDate関数を使用して生成した為 それに合わせて月と日がそれぞれ2桁になるように加工しています。

対象の日付リストを探し出す時に文字列で一致させて探し出すので 検索用に対象の日付をリストのタイトルに合わせて加工し 文字列として変数に入れて用意しておきます。

Slackに通知したい対象のリストとカードを検索する


    // 対象のリストのidを格納する変数
    let target_id

    // slackにpostする対象の日付と一致したリストのIDを取得
    for (i = 0; i < listjson.length; i++) {
        if (listjson[i]['name'].match(search_target_string)) {
            target_id = listjson[i]['id']
            post_date_string = listjson[i]['name']
        }
    }

    // slackにpostする対象の日付のリストにあるカードを「target」シートに出力
    for (i = 0; i < cardjson.length; i++) {
        if (cardjson[i]['idList'].match(target_id)) {
            sheet.getSheetByName('target').getRange(post_card_num+2,3).setValue(cardjson[i]['name']); // カード名をシートに記載
            sheet.getSheetByName('target').getRange(post_card_num+2,4).setValue(cardjson[i]['shortUrl']); // カードURLをシートに記載

            post_card_num += 1
        }
    }

冒頭で取得したリスト情報の中から先ほど生成した検索用文字列を使用し 対象の日付のリストを探し そのリストのIDを変数に入れ保存します。

次に同じく冒頭で取得したカード情報の中から対象のリストIDと一致するカードを取得し、スプレッドシートに記録します。

Slackに通知する

遂にSlackに通知する時がやってきました……!


    let post_data = ['<!channel>'];

    //メッセージをSlackに送る
    if(post_card_num > 0) {
        post_data.push(post_string + '' + post_date_string + 'の予定だよ!!');
        for (i = 0; i < post_card_num; i++) {
            let cardname = sheet.getSheetByName('target').getRange(i+2,3).getValue(); // カード名
            let cardurl = sheet.getSheetByName('target').getRange(i+2,4).getValue(); // カードURL

            post_data.push('=================\n- カード名:' + cardname +'\n- カードURL:'+ cardurl);
        }
    } else {
        post_data.push(post_string + ' ' + post_date_string + 'の予定はありません!!');
    }

    const sentence = post_data.join('\r\n');
    const payload = {'text' : sentence,};
    const options = {
        'method' : 'post' ,
        'contentType' : 'application/json' ,
        'payload' : JSON.stringify(payload),
        'link_names' : 1,
    };

    // 投稿するwebhookURL
    const url = prop.slack;

    UrlFetchApp.fetch(url, options);

post_dataというSlackに通知したい情報を格納する配列を用意して、スプレッドシートに記載されている数だけ配列にどんどん情報を収納していきます。

最後にSlackにPOSTする際のオプション設定を整えてwebhookURLへ投稿!ドーン ふう・・・・(笑)

次回の通知トリガーを設定する

GASで設定できる定期実行では、時間指定の場合は1時間のブレがある & 細かな時間指定だと毎日実行できない などの制限がある為 スクリプトでトリガーの設定を行います。

9am / 7pm / 23pm それぞれの通知が行われた際にトリガーの設置メソッドも呼び出し、次回の通知トリガーを設定します!以下のメソッドを作成しmain()の中で呼び出すことで、通知が行われる度に次回の通知の設定を行います。

function setTrigger() {
    let setTime = new Date();
    const current_time = setTime.getHours();

    if (0 <= current_time && current_time < 9) {
        // 基本的にここの処理は通らないはず
        setTime.setHours(9);
        setTime.setMinutes(00);
    } else if (9 <= current_time && current_time < 19) {
        setTime.setHours(19);
        setTime.setMinutes(00);
    } else if (19 <= current_time && current_time < 23) {
        setTime.setHours(23);
        setTime.setMinutes(00);
    } else {
        setTime.setDate(setTime.getDate() + 1)
        setTime.setHours(9);
        setTime.setMinutes(00);
    }

    ScriptApp.newTrigger('main').timeBased().at(setTime).create();
}

🎉 実際にコードを動かしてみよう

はい!!!!だいぶ息切れしてきましたが。。。。(笑)やっと実際に実行する時がやってきました。。。!!!!イエイ!!!!

書いただけだと実行されないので、上記で作成したスクリプトのmain関数を1度だけ実行してみましょう!そうすることで、次回の通知トリガーも設定され 以降は自動で定期的に通知が飛んできます👏👏

どうでしょうか?Slackに通知が飛んできたら成功です🎉!

Trelloカレンダー記事第三弾目は以上になります!プログラムを使って自分の使いやすいようにしていくのは楽しいですね〜書いてる本人は満足です🤤www

最後まで読んでくださりありがとうございました!