LoginSignup
1
1

More than 5 years have passed since last update.

slackでメンション飛ばしてうさぎのエサと体重を記録する

Posted at

概要

  • ウサギを飼うときの体重、チモシー、ペレット(通称カリカリ)の記録をslackでやる
  • 体重とチモシーとペレットの平均値と標準偏差を予め入力しておくことで、入力された数値からいずれに該当するか自動的に判定します。
  • slackからのメンションをgoogle cloud functionで受けて、google datastoreに保存して、slackにメッセージします。

動作イメージ

Image from Gyazo

手順

index.js
const PROJECT_ID = 'google datastoreのプロジェクトID';
const BOT_USER_TOKEN = "slackのBot User OAuth Access Token";
const GOOGLE_APPLICATION_CREDENTIALS = "./アプリケーションのデフォルト認証情報.json"; // https://cloud.google.com/docs/authentication?hl=ja

const startOfToday = require('date-fns/start_of_today');
const format = require('date-fns/format')
const request = require('request-promise-native');
// const payloadTest = require('./payloadTest.json'); テスト用
const Datastore = require('@google-cloud/datastore');
const datastore = Datastore({projectId: PROJECT_ID});

// 環境変数を設定する(簡易的です。.envでやりましょうー)
const set_env = () => {
  process.env.BOT_USER_TOKEN = BOT_USER_TOKEN;
  process.env.GOOGLE_APPLICATION_CREDENTIALS = GOOGLE_APPLICATION_CREDENTIALS;
}

// slackに返信する
const postMessage = (payload) => {
  request.post('https://slack.com/api/chat.postMessage', {
    headers: {'Authorization': `Bearer ${process.env.BOT_USER_TOKEN}`},
    json: payload,
  });
}

// いわゆる。zスコア計算する。小さいほど、その(体重、チモシー、カリカリ)分布から得られる値と判断する
// 今回は不要だけど累積分布関数でp値だせばよりそれっぽい。
const zScore = (x, mean, stdev) => {
  const z = (x - mean) / stdev;
  return z
}

// 入力値を保存する
async function addRecord(kind, value, unit, message) {
  const date = startOfToday();
  const dateStr = format(date, 'YYYY-MM-DD');
  const recordKey = datastore.key([kind, dateStr]);
  const data = [
    {
      name: 'date',
      value: date,
    },
    {
      name: 'unit',
      value: unit,
    },
    {
      name: 'value',
      value: value,
    },
    {
      name: 'message',
      value: message,
      excludeFromIndexes: true,
    },
  ];
  const entity = {
    key: recordKey,
    data: data,
  };
  datastore.save(entity);
}


// 体重、チモシー、カリカリのパラメータ(平均値、標準偏差)を取得する
const getEntities = (kind) => {
  const query = datastore.createQuery(kind)
    .limit(10);
  return datastore.runQuery(query)
}

const onRequest = async (req, res) => {
  // slackからの入力値を取得する
  let payload = req.body;
  // let payload = payloadTest;

  // APIキーなどを環境変数にセットする
  set_env();

  // slackからのverificationのため。
  // https://qiita.com/castaneai/items/144bb17ec8bb21f306d4
  if (payload.type === 'url_verification') {
    return res.status(200).json({'challenge': payload.challenge});
  }

  let kind = '';
  let z = 9999;
  const message = payload.event.text
  const value = message.match(/> [^0-9]*([0-9]+[\.]?[0-9]*)/) && message.match(/> [^0-9]*([0-9]+[\.]?[0-9]*)/)[1]; // 数値を抽出

  // valueに数値が入っていない場合は何もせず返す
  if (!value || !(value > 0) || !(payload.event.type === 'app_mention')) {
    postMessage({
      text: `<@${payload.event.user}> 数字入れてね`,
      channel: payload.event.channel,
    });
    return res.status(200).send('OK');
  }

  // 数値が 体重、チモシー、カリカリのどの平均値に近いか判定して
  // slackで返事 & データを保存
  getEntities('Parameter')
    .then(results => {
      const entities = results[0];
      let selectedEntity = {};
      entities.forEach(entity => {
        if (Math.abs(z) > Math.abs(zScore(value, entity.mean, entity.stdev))) {
          selectedEntity = entity;
          z = zScore(value, entity.mean, entity.stdev);
        }
      });
      addRecord(selectedEntity.kind, value, 'g', message);
      const date = startOfToday();
      const dateStr = format(date, 'YYYY-MM-DD');
      postMessage({
        text: `<@${payload.event.user}> 入力ありがと。${dateStr}${selectedEntity.name}${value} g で記録しておくね。`,
        channel: payload.event.channel,
      });
    });

  return res.status(200).send('OK');
}

exports.slackChoicesBot = onRequest;

以上。

1
1
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
1
1