Google Apps ScriptでGitHubのProjectsの内容をSlackに流す
またGASネタです。GASの記事ばかり書いてて、GAS芸人になりつつあります。
下記の記事を見て、個人的に研究のTodoをProjectsで管理していて、それをSlackに流している話を書こうと思いました。
GASなのでGitHubにコードをあげていないのですが、雰囲気だけ記録したいと思います。
とりあえず見た目ですが、まずProjectsがあって、
変更があったりすると、Slack botがこんな感じでreportしてくれます。
この子(bot)がやってるのは、
- Projectsで「カード追加」「カード移動」があった時にその内容をSlackに流す
- GASのトリガーを使って毎朝Projectsの内容を(長かった場合は省略して)流す
です。
ProjectsのColumnやCardsをGitHub APIで取ってきてGAS経由でSlackに流しています。
かなり個人利用を目的にしたものなので、チーム用途には向いてない感じですね。
何かの参考になれば幸いです。
作り方
各種セットアップ
GitHubのリポジトリ作成、Projectsの作成、APIトークン取得は済んでるものとします。
Permissionはrepo
だけチェックしておけばok。
トークンをメモったら、cURLでプロジェクトのURLとidを取得します。
OWENER="Your GitHub Account Name" REPOSITORY="Repository Name" TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" curl \ -H "Accept: application/vnd.github.inertia-preview+json" \ -H "Authorization: token ${TOKEN}" \ "https://api.github.com/repos/${OWENER}/${REPOSITORY}/projects"
レスポンスのhtml_urlとidをメモっておきます。次に、このidを使ってcolumn idを取得します。
TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" PROJECT_ID="xxxxxxx" curl \ -H "Accept: application/vnd.github.inertia-preview+json" \ -H "Authorization: token ${TOKEN}" \ "https://api.github.com/projects/${PROJECT_ID}/columns"
レスポンスのidとnameのペアをメモしておきます。
次に、この辺を参考にSlackのWebhookを用意します。
これで、GitHub側とSlack側の準備は完了です。
GAS
GASを作っていきます。
まずは、プロパティを埋めていきます。PropertiesServiceとか使っても良いと思いますが、ここでは簡単に変数に格納します。channel、botname、iconemoji、title、colorは任意です。
var properties = { "github_api": "https://api.github.com", "github_api_token": "xxxxx", "github_secret_key": "xxxxxx", "proj_name": "hoge", "proj_url": "https://github.com/walkingmask/hoge/projects/1?fullscreen=true", "proj_columns": [ { "title": "To do😡", "id": "xxx", "color": "danger" }, { "title": "In progress😨", "id": "xxx", "color": "warning" }, { "title": "Done😇", "id": "xxx", "color": "good" } ], "slack_web_hook": "https://hooks.slack.com/services/xxx", "slack_channel": "#_hoge", "slack_bot_name": "Progress of walkingmask", "slack_icon_emoji": ":walkingmask:", };
Projectsから色々取ってきて整形する感じのものを定義します。
// Projectsのカラムに含まれるカードを取得するやつ function getCards(api, column_id, token) { var cards = []; var url = api + "/projects/columns/" + column_id + "/cards"; var options = { "headers": { "Accept": "application/vnd.github.inertia-preview+json", // 試験段階だから特殊なヘッダーが必要 "Authorization": "token " + token, } } var response = UrlFetchApp.fetch(url, options); response = JSON.parse(response.getContentText()); for (var i in response) { cards.push(response[i].note); } if (1 > cards.length) { // 何もなかった cards.push("( ᐛ👐) パァ"); } return cards; } // Projectsの内容を取得するやつ function getKanban() { var columns = properties.proj_columns; var api = properties.github_api; var token = properties.github_api_token; var kanban = []; for (var i in columns) { var cards = getCards(api, columns[i].id, token); kanban.push({"title": columns[i].title, "cards": cards, "color": columns[i].color}); } return kanban; } // カードを整形するやつ function formatCards(cards) { var num_cards = cards.length; var formated_cards = ""; for (var i=0; i < (num_cards>3?3:num_cards); i++) { formated_cards += "• " + cards[i] + "\n"; } if (num_cards > 3) { var rest = num_cards - 3; formated_cards += "and "+rest+" items👍\n"; } return formated_cards; } // Projectsを整形するやつ function formatKanban(kanban) { var attachments = []; for (var i in kanban) { var formated_cards = formatCards(kanban[i].cards); attachments.push({ "fallback": kanban[i].title, "color": kanban[i].color, "fields":[ { "title": kanban[i].title, "value": formatCards(kanban[i].cards), "short": false } ] }); } return attachments; }
Slackに投げる関数を定義します。
function postSlack(title, attachments) { var payload = { "channel": properties.slack_channel, "username": properties.slack_bot_name, "icon_emoji": properties.slack_icon_emoji, "text": title + " <" + properties.proj_url + "|[URL]>", "attachments": attachments, }; var options = { "method": "post", "contentType": "application/json", "payload": JSON.stringify(payload) }; UrlFetchApp.fetch(properties.slack_web_hook, options); }
あとは、Projectsの変更に応じて動いたり、日報するための関数を定義します。
// GitHubのHook(Projectsの変更)からPOST受けたら動くやつ function doPost(request){ eventReport(request.parameter.payload); } // Projectsの変更をSlackに流すやつ function eventReport(payload) { var payload_json = JSON.parse(payload); var action = payload_json.action; var column = id2Title(payload_json.project_card.column_id, properties.proj_columns); var value = "• " + payload_json.project_card.note; var field_title; if (action == "created") { field_title = "Created new task in " + column; } else if (action == "edited") { field_title = "Edited task in " + column; } else if (action == "moved") { var changes = payload_json.changes; if (!changes) { return; } var from_column = id2Title(changes.column_id.from, properties.proj_columns); value += "\n" + from_column + " :arrow_right: " + column; field_title = "Moved task"; } else { return; } var attachments = [{ "fallback": "Event report", "color": "#439FE0", "fields": [ { "title": field_title, "value": value, "short": false } ] }]; postSlack("Event report", attachments); } // 日報するやつ(トリガーで動かす) function dailyReport() { var kanban = getKanban(); var formated_kanban = formatKanban(kanban); postSlack("Daily report", formated_kanban); }
dailyReportは朝とかに動くようにトリガーを設定してあげてください。
可愛い便利関数はこちら。
function id2Title(column_id, columns) { for (var i in columns) { if (column_id == columns[i].id) { return columns[i].title; } } }
まとめ
書いてる間に何で作ったんだろとか思ってたんですが、自分の研究の進捗を研究室メンバーにシェアするために作ったことを思い出しました。
(最近研究に進捗がなくて毎日同じレポートをするだけbotになっている...)
このbot自体は汎用性低いと思うのですが、GitHubでProjectsのAPIを叩いたり、columnやcardsを整形したりといった部分が参考になれば良いなと思います。
GitHubのProjects自体はTodo管理としてはシンプルでありながらも使いやすいなと感じてます。
あとは、issuesと連携するような動きを追加するともっと面白そうですね。
APIがプレビュー段階なので、今後の動向にも注目です。