また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がプレビュー段階なので、今後の動向にも注目です。