非エンジニアのオペレーターの人のために管理画面を作るのが面倒で、
Slack + Googleスプレッドシートでデータを更新作業をしてくれるHubotを作ったら結構良かった
例えば、こんな感じのランキングモジュールがありました。
システムや運用の都合で自動化せずに、非エンジニアの人が手動で毎週更新するという仕様です。
更新の流れ
1.データをGoogleスプレッドシートに記入する。
> 表示させたいコンテンツに対応するIDを記入してもらう2.SlackからHubotで更新させる。
> Hubotへメンションを送るだけ3.おわり
オペレーターの作業はこれだけ
普段使っているツールなので馴染みやすいです
仕組み
SlackのメンションをHubotで受け付け、GASでスプレッドシートの内容からJSONを生成、そのJSONをStaticサーバーにアップロードし、そのデータをサービス側のサーバーから取得するという流れになります。
1.Slackのメンションを受け取る
hubot-slackのライブラリを読み込む
{
"dependencies": {
"hubot-slack": "^3.4.2",
"hubot-slack-attachment": "^1.0.0",
}
}
Slackのトークンを設定
$ HUBOT_SLACK_TOKEN="xxxxxxxxxxx"
Slackからメンションを受け取るにはこれだけ
module.exports = (robot) ->
robot.respond /ランキングデータ更新/i, (msg) ->
console.log "更新依頼受付"
これでランキングデータ更新
の文字列が含まれるときに実行されるようになります。
2.GASでスプレッドシートのデータを取得するAPIを作る
こんなかんじの構成のシートがあったとします
monthly | weekly |
---|---|
11111 | 66666 |
22222 | 77777 |
33333 | 88888 |
44444 | 99999 |
55555 | 12345 |
取得生成コード
function doGet(e) {
var url = "https://docs.google.com/spreadsheets/d/xxxxxx/edit#gid=0"; // 取得ファイルのURL
var sheetName = "sheet1"; // 取得するシート名
var book = SpreadsheetApp.openByUrl(url);
var sheet = book.getSheetByName(sheetName);
var json = getData(sheet);
var res = ContentService.createTextOutput(JSON.stringify(json));
return res.setMimeType(ContentService.MimeType.JSON);
}
function getData(sheet) {
var monthly = [];
var weekly = [];
var lastRow = sheet.getLastRow();
for(var rowIndex=2; rowIndex<=lastRow; rowIndex++) {
var month = sheet.getRange(rowIndex, 1);
var week = sheet.getRange(rowIndex, 2);
if (month.getValue()) {
monthly.push(month.getValue());
}
if (week.getValue()) {
weekly.push(week.getValue());
}
}
return {"monthy":monthly, "weekly":weekly};
}
公開
→ ウェブアプリケーションとして導入
でAPI化
Hubotからのアクセスが面倒なのでとりあえず、```全員(匿名ユーザー含む)```にします。
できあがったAPIを叩くと、こんなJSONが返却されるはず
{
"monthy": [
11111,
22222,
33333,
44444,
55555
],
"weekly": [
66666,
77777,
88888,
99999,
12345
]
}
3.HubotからGAS APIを取得してファイルを保存
npmのrequest
、fs
を読み込む
request = require 'request'
fs = require('fs')
取得保存のコード
sheetApi = "https://script.google.com/macros/s/xxxxx-/exec" # GASで生成したAPI
tmpFile = "/tmp/ranking_" + new Date().getTime() + ".json" # 仮保存用ファイルのパス
request {
url: sheetApi
}, (err, response, body) ->
if err
console.error err
return
# json形式が正しいかチェック
JSON.parse(body)
# ファイルとして保存
fs.writeFileSync(tmpFile, body)
4.HubotからSCPでStaticサーバーへアップロード
NginxでStaticサーバーを用意し、もしGoogle側で何かあったとしても吸収できる構造にします。
npmのscp2
を読み込む
client = require("scp2")
アップロードして、成功したらSlackへ成功通知
client.scp(
tmpFile,
{
host: "127.0.0.1",
username: "username",
password: "password",
path: "/var/www/nginx/public/ranking.json"
},
(err) ->
if err
console.error err
return
data = {
text: "ランキングデータを更新いたしました!"
channel: "test" #通知するチャンネル名
}
robot.emit "slack.attachment", data
)
この部分はGit+capistranoとかにすれば、もっと良い感じになるかなと思います。
5.サービス側からStaticサーバーのranking.jsonを取得
普段と同じように、APIを取得する感じで書いて、クライアントに表示する
おわり
Slackをインターフェイスにしたら、使い方を教えやすいし、わかりやすい、
オペレーター業務がSlack上に流れるので更新されたタイミングが残るのも良かったです。
ただフォーマットがズレるとエラーになったりするため(上記の例だとIDの数値ではなく、文字列が入ってしまったなど)、エラーチェックなどのオペレーションミスを防ぐ仕組みはなにかしら必要です。
手順は多いですが、ローテクの寄せ集めなので作るのは結構簡単です。
どっちもHubotもGASもECMAScript仕様なので覚えなきゃいけないことは少なく
なにより、この機能のために管理画面のUIやDBを用意しなくても良いのが楽。