LoginSignup
16
15

More than 5 years have passed since last update.

Botもサーバーも不要!SlackとGASで簡単な共有&アンケートツールを作ってみた

Last updated at Posted at 2018-02-21

こんちには、エバーセンスの江上です。

Slackつかってますか?僕は /remind に幾度救われたかわかりません。最近1年前に設定したremindのおかげで、プロバイダーから3000円返ってきました。
そんなremindのメッセージにボタンがついてるの知ってますか?
(↓のMark as CompleteとかDeleteとか)
message button.png

この超便利機能を、自分でも作れると聞いて、早速試してみました ^o^

ちなみに今回は、Botもサーバーも運用が大変なので、 Google App Script に全てお任せしています。
よって、foreverがハングしたりec2が急に落ちたりする世界とは無縁で運用できます!
Googleさんありがとー。

必要なもの

  • googleアカウント
  • slackアカウント

参考情報

やってみる

とりあえず、『毎日「今日の小技」をSlackに共有して、それが役に立ったかどうかを回答してもらう』システムをつくることにしました。

やることはこんな感じ
1. Slackに「今日の小技」を通知する
2. ユーザーがボタンを押したらフィードバックが送信されてスプレッドシートにデータをためる

1. Slackに「今日の小技」を通知する

1. Slack Appを作成する

Slack Appの管理画面でサクッと作成します
create slack app.png

2. Incoming Webhookを設定します

作成したSlack Appを選択して、通知したいチャンネルを選べばOKです。終わったら、自分のworkspaceにインストールします
setup incoming webhook.png

3. GASでSlackに通知します

まずは「今日の小技」を書いたスプレッドシートを用意します
こんな感じ
こんな感じ3.png

次は、スプレッドシートのデータを取得して、Slackに通知します
(getColNumなどの関数は別途定義してます。こちらでソース公開してるのでそっちで確認してください )

// SpreadSheetに記載された小技を日々通知する
function notify(){
  var row = getRowNumByDate(new Date())
  // 該当がなければスキップ
  if(row === 0){
    return;
  }

  var elements = getNotifyMessageElements(row);
  var title = getNotifyMessageForSlack(elements);
  var attachments = getNotifyAttachments(elements);

  sendToSlack(title, attachments)
}

function getNotifyMessageForSlack(e){
  return "*" + e.title + "*" + "\n\n" +
    e.description + "\n" +
    "by" + e.author;
}

function getNotifyAttachments(e){
    var actions = [
                {
                    "name": "feedback",
                    "text": "役に立った",
                    "style": "primary",
                    "type": "button",
                    "value": FEEDBACK_TYPES.GREAT
                },
                {
                    "name": "feedback",
                    "text": "使えない",
                    "style": "danger",
                    "type": "button",
                    "value": FEEDBACK_TYPES.BAD
                },
                {
                    "name": "feedback",
                    "text": "知ってた",
                    "type": "button",
                    "value": FEEDBACK_TYPES.KNOWN
                }
            ]
  return [
        {
            "fallback": "もう一回おしてね",
            "callback_id": e.id,
            "color": "#3AA3E3",
            "attachment_type": "default",
            "actions": actions
        }
    ]
}

function getNotifyMessageElements(row){
  var sheet = SpreadsheetApp.getActive().getSheetByName(TARGET_SHEET);
  var id = sheet.getRange(row, getColNum("id")).getValue()
  var title = sheet.getRange(row, getColNum("title")).getValue()
  var description = sheet.getRange(row, getColNum("description")).getValue()
  var author = sheet.getRange(row, getColNum("author")).getValue()

  return {id: id, title: title, description: description, author: author};
}

function sendToSlack(text, attachments){
  // username, channel, iconはSlack App側で設定したもので上書きされるので書かない
  var payload = {
                 "text": text,
                 "attachments": attachments,
                 "link_names": 1
                };

  var options = {
    "method" : "post",
    "payload" : JSON.stringify(payload)
  };

  UrlFetchApp.fetch(SLACK_WEBHOOK_URL, options);
}

で、 notify 関数を実行すると

slackに通知.png

きました!ボタン付きのメッセージが!!!
先ほど、attachmentsのactionで定義したものがボタンになってます。

次は、これらのボタンを押したら、スプレッドシートに「役に立ったかどうか?」がたまるようにしてみましょう

2. ユーザーがボタンを押したらフィードバックが送信されてスプレッドシートにデータがたまるようにする

1. GASをウェブアプリケーションとして公開します

スプレッドシートにデータをためるには、GASをウェブアプリとして公開します。
こちらがよくまとまっているので参考にしてみてください。
GASでwebアプリケーションを作る方法

ウェブアプリとして公開するとhttps://script.google.com/から始まるurlが発行されるのでメモしておきます

2. Slack AppのInteractive Componentsを設定します

次に、Slack Appの Interactive Components ページにおいて、Request URLを設定します。urlは先ほど取得したhttps://script.google.com/から始まるurlです
setup_spreadsheet_url.png

3. エンドポイントとなるGASを作成します

最後に、GASを作成しましょう

function doPost(e){
  var json = JSON.parse(e.parameter.payload)
  addFeedback(json)


  var feedbackType = getFeedbackType(json.actions)
  var responsePayload = getResponsePayloadByFeedbackType(feedbackType);
  responseToSlack(json.response_url,responsePayload);

  // テキストを返すと送ったメッセージが上書きされてしまうので空を返す
  return ContentService.createTextOutput();
}

function addFeedback(json){
  var feedbackType = getFeedbackType(json.actions)
  var sheet = SpreadsheetApp.getActive().getSheetByName(TARGET_SHEET);
  var row = getRowNumById(json.callback_id);

  Object.keys(FEEDBACK_TYPES).forEach(function (key) {
    var col = getColNum(FEEDBACK_TYPES[key]);
    var range = sheet.getRange(row, col);

    if(FEEDBACK_TYPES[key] === feedbackType){
      appendName(range, json.user.name)
    } else {
      subName(range, json.user.name)
    }
  });
}

function appendName(range, name){
  var original_value = range.getValue()
  var names = original_value.split(",");
  names.push(name)
  var uniqueNames = names.filter(function (x, i, self) {
            return self.indexOf(x) === i && x !== "";
        });

  range.setValue(uniqueNames.join(","))
}

function subName(range, name){
  var original_value = range.getValue()
  var names = original_value.split(",");
  var index = names.indexOf(name);
  if(index >= 0){
    names.splice(index,1)
  }
  range.setValue(names.join(","))
}

function getFeedbackType(actions){
  var type = FEEDBACK_TYPES.KNOWN;
  if(actions.filter(function(e){ return e.name == "feedback" && e.value == FEEDBACK_TYPES.GREAT }).length > 0) {
    type = FEEDBACK_TYPES.GREAT;
  } else if(actions.filter(function(e){ return e.name == "feedback" && e.value == FEEDBACK_TYPES.BAD }).length > 0) {
    type = FEEDBACK_TYPES.BAD;
  }
  return type;
}

function getRowNumById(id){
  var sheet = SpreadsheetApp.getActive().getSheetByName(TARGET_SHEET);
  var range = sheet.getRange("A2:A");
  var values = range.getValues();
  var row = 0;
  for(i=0;i<values.length;i++){
    if(values[i][0] == id){
      row = i;
      break;
    }
  }
  return row + ROW_OFFSET;
}

function getResponsePayloadByFeedbackType(feedbackType){
  var payload = {
    "response_type": "ephemeral",
    "replace_original": false,
    "text": "text"
  }

  switch(feedbackType){
    case FEEDBACK_TYPES.GREAT:
      payload.text = "ありがとうございます。お役にたててよかったです :joy: ";
      break;
    case FEEDBACK_TYPES.BAD:
      payload.text = "ありがとうございます。明日に期待してください :smile: ";
      break;
    case FEEDBACK_TYPES.KNOWN:
      payload.text = "ありがとうございます。さすがです :laughing: ";
      break;
  }

  return payload;
}

function responseToSlack(responseUrl, payload){
  var options = {
    "method" : "post",
    "payload" : JSON.stringify(payload)
  };

  UrlFetchApp.fetch(responseUrl, options);
}

さぁ、準備は整いました。再度GASをウェブアプリとして公開し、Slack Appも再インストールしたら、notifyを実行して、メッセージを送ります。そして、「知ってた」を押すと。。。
ボタンを押したらフィードバック送信.png

見事Slack Appから返信がきました!他のボタンを押すとまた違ったメッセージがかえってきます

そして、スプレッドシートをみてみると
フィードバック取得.png
役に立ったか?のフィードバックも記録されてます!

最後に

めでたく毎日「今日の小技」がSlackに流れてきて、どれほど役に立ったのかを測るところまで仕組み化できました。
GASでは定期実行もできるので毎日流すことも可能です。githubでは最後に何人の役に立ったのかをSlackに投稿するところまで自動化しているのでみてみてください。

そして、今回は業務として投稿してるので、宣伝です。
エバーセンスでは、技術で家族を幸せにしてくれる仲間を募集しています。よかったら応募してね。おまちしてます
https://www.wantedly.com/companies/eversense-co/projects

16
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
15