当番をランダムに決めて通知してくれるSlack BotをGoogle Apps Scriptで作った

研究室での毎週の当番決めを自動化したいと思い、Google Apps Script(GAS)を使ったSlack botを作成しました。

すでに作ってる人いるだろうなと検索したましたが、スプレッドシートから指定の人数をランダムにピックアップして当番を回していくGASが見つけられなかったです(スプレッドシートの全員に毎回割り当てるGASを書いている人は結構いたのですが)。

なのでこの記事では、作成したSlack Bot用のGASの公開と解説をしようと思います(Slack Botへの連携はこちらのサイトをご覧ください)。

Slack Botの使い方

最初にSlack Botの使い方を載せておきます。とにかく使えれば良いという人は

  1. スプレッドシートの作成
  2. 作成したシートのスクリプトエディタに下記のスクリプトをコピペ
  3. Settingに必要な情報の記入

をすれば使えます。それ以降で、GASを書いていて思ったこととかをつらつらと書いていきます。

用意するスプレッドシートのフォーマット

1列目:SlackのメンバーID(<>で括る)
2列目:SlackのUser名
3列目:選択されたかどうかのマスク値(最初は1に設定する)(0: 割り当て済み, 1: 割当されていない)

Google Apps Script

doPost() がメインの処理になります。3~7行目に必要情報を追記してください。

role_assignment.gs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
function doPost(e) {
// Setting
const spreadSheetId = '';
const sheetName = ''
const pickNum = 2; // The number of members you wanna pick up
const slackWebhookUrl = '';
const isTest = true // set 'false' to deploy

// Get Spread Sheet (Slack ID, User name, Mask label)
const masterSheet = SpreadsheetApp.openById(spreadSheetId).getSheetByName(sheetName);
const lastRow = masterSheet.getLastRow();
const vals = masterSheet.getRange(1, 1, lastRow, 3).getValues();

var ids = vals.getColumnFrom2DArray(0);
var mask = vals.getColumnFrom2DArray(2);
var maskedIds = ids.maskFilter(mask.map(Boolean));
var usedIds = ids.filter(function (v) {return maskedIds.indexOf(v) === -1;});

const today = isTest ? new Date('Mon Jan 20 2020') : new Date();
if (today.getDay() === 1) { // post a message on Monday
var sampledIds = randomSample(maskedIds.slice(), pickNum);
updateMask(masterSheet, ids, sampledIds);
var updated_mask = masterSheet.getRange(1, 3, lastRow).getValues();
if (updated_mask.isAllZero()) {
resetMask(masterSheet, lastRow);
}
if (sampledIds.length < pickNum) {
var additional_ids = randomSample(usedIds.slice(), pickNum-sampledIds.length);
updateMask(masterSheet, ids, additional_ids);
sampledIds = sampledIds.concat(additional_ids);
}
const post = createPost(sampledIds, today);
UrlFetchApp.fetch(slackWebhookUrl, post);
}
}
//
Array.prototype.maskFilter = function(mask) {
return this.filter(function(v, i) {return mask[i];});
}

Array.prototype.getColumnFrom2DArray = function(col_idx) {
return this.map(function(v) {return v[col_idx];});
}

Array.prototype.isAllZero = function() {
return this.every(function(v) {return v[0] === 0;});
}

function updateMask(sheet, master_ids, sampledIds) {
sampledIds.forEach(function(i) {
sheet.getRange(master_ids.indexOf(i)+1, 3).setValue(0);
})
}

function resetMask(sheet, lastRow) {
var range = sheet.getRange(1, 3, lastRow);
var ones = [];
for (var i=0; i < lastRow; i++) {
ones[i] = [1];
}
range.setValues(ones);
}

function createPost(id_arr, day) {
var template = "%s\n%s ~ %s\n It's your turn to ####.\n";
var message = Utilities.formatString(template, id_arr.join(" "), formatDate(day, 0), formatDate(day, 4));
var payload = {
'username': 'lab admin',
'channel': '#test',
'text': message
}
var options = {
'method': 'POST',
'Content-type': 'application/json',
'payload': JSON.stringify(payload)
}
return options;
}

function randomSample(arr, num) {
var newArr = [];
var rand = 0;
while (newArr.length < num && arr.length > 0) {
rand = Math.floor(Math.random() * arr.length);
newArr.push(arr[rand]);
arr.splice(rand, 1);
}
return newArr;
}

function formatDate(day, increment) {
day.setDate(day.getDate() + increment);
return Utilities.formatDate(day, 'JST', 'MM/dd (E)');
}

Settingに記載する情報

Slack Botの設定

参照サイトのやり方を見てください。

通知されるメッセージ

通知されるメッセージは以下の通りです。メッセージを編集する場合は、65行目の template を編集してください。

感想

JavaScriptはほとんど書いたことがなかったので、PythonやJavaと対応する処理ができないかなと思いながら調べつつ書いていきました (なので非効率的な処理とかも多くあると思うので、あればぜひご指摘お願いします)。 また、GASがJavaScript 1.6ベースで作られているため、それ以降で実装されている機能が使えないのも割と不便だと感じました(例えばアロー関数)。

このSlack Botの作成を通して、普段自分がいかにPythonが色々な機能・ライブラリを使っているのかを痛感しました。 Pythonではパッとできるのになあという処理ができないことが多くて、それの実装を追加したのもいい勉強になったなと思います。

以上でこの記事を終わります。

【追記】
詳解! Google Apps Script完全入門 ~Google Apps & G Suiteの最新プログラミングガイド~という本を買って読んでみました。 私のようなGASやJava Script初心者にとってはどちらについても初歩から理解することができたので良かったです。 GASに関してはあまりPythonとかに比べて記事も少ないので、辞書代わりに手元にあると便利です。

参照