Techブログ - MNTSQ, Ltd.

リーガルテック・カンパニー「MNTSQ(モンテスキュー)」のTechブログです。

MNTSQのSlackに住まうbotたち

MNTSQ Tech Blog TOP > 記事一覧 > MNTSQのSlackに住まうbotたち

f:id:takumi-hirata-mntsq:20210827091656j:plain

いろいろなbot

組織をスケールさせる上でコーポレートエンジニアリングは非常に重要である。MNTSQではissue-drivenで誰でも気軽にbotを作ることができる。現在MNTSQのSlackにいるいくつかのbotを紹介しよう。

施錠と消灯を催促するbot

f:id:takumi-hirata-mntsq:20210825131812p:plain

観葉植物の水やりを催促するbot(ガイド付き)

f:id:takumi-hirata-mntsq:20210827085158p:plain

社員8名ぶんのサラダを社長に取りに行かせるbot(実際には当番制)

f:id:takumi-hirata-mntsq:20210827085228p:plain 詳細はこの記事で紹介している。

にゃーんと言うと何かを返すbot

f:id:takumi-hirata-mntsq:20210827085418p:plain

デイリーの共有会でグループ分けをするbot

f:id:takumi-hirata-mntsq:20210827085434p:plain

セキュリティチェックの案内を出すbot

f:id:takumi-hirata-mntsq:20210827085500p:plain

以下では「デイリーの共有会でグループ分けをするbot」の仕組みを紹介する。

デイリーの共有会でグループ分けをするbot

MNTSQでは毎日14:00-14:15でDaily-syncという共有会を行っている。Daily-syncは異職種間コラボレーションの質を高めるために、互いが何を何故やっているか共有しfeedbackしあう場である。以前はメンバー全員の状況を共有していたが、社員数の増加に伴いグループ分けする運びとなった。

このbotはDaily-syncのグループ分けを自動化するために作られた。

グループ分けの流れ

1. [bot] Daily-syncの1時間前にリマインドを送る

f:id:takumi-hirata-mntsq:20210827085920p:plain 「参加します」スタンプを押すよう催促しているが、ダークテーマだと可視性がとても低い。

2. [参加者] 共有内容をSlackに投稿しスタンプをつける

f:id:takumi-hirata-mntsq:20210827085942p:plain Daily-syncではこの内容をベースに議論する。

3. [bot] グループごとに場所と参加者が通知する

f:id:takumi-hirata-mntsq:20210827090002p:plain

スタンプをつけ忘れると抽選から漏れてしまうので注意が必要だ。 f:id:takumi-hirata-mntsq:20210827090021p:plain

botの仕組み

Google Apps Script (GAS)を使う。私は普段Pythonを使うことが多いので、行末のセミコロン付け忘れを克服するのに苦戦した。GASの基本的な使い方は他の記事に譲るとして、ここではポイントだけ記しておく。

使用するSlack API

これらのAPIが使えるように、事前にトークンの発行や権限設定を済ませておく。

  • conversations.history: 当日の参加者の投稿を集める
  • chat.postMessage: リマインドやグループ分けの結果を投稿する 当日の投稿を集める

conversations.historyoldest パラメータを使えば当日の投稿を集められる。 oldest にはSlackのタイムスタンプ形式で渡す。例えば2021-08-26は 1629903600 である。

  let now = new Date();
  let y = now.getFullYear();
  let m = now.getMonth();
  let d = now.getDate();
  let ts = (new Date(y, m, d).getTime() / 1000).toString();
  let options = {
    "method": "get",
    "payload": {
      "token": token,
      "channel": "C0123456789",
      "limit": 1000,
      "oldest": ts
    }
  };
  let response = UrlFetchApp.fetch("https://slack.com/api/conversations.history",options).getContentText();
  let data = JSON.parse(response);

特定のスタンプがついた投稿を集める

conversations.history の結果をfilterする。

  let participants = data["messages"].filter(message => {
    return ("reactions" in message) && (message["reactions"].filter(reaction => {
      return reaction["name"] === "sankashimasu"; // :sankashimasu: がついてる人
    }).length > 0);
  });

グループ分けする

なるべく職種でバラけるようにランダムにグループ分けする。

  1. 参加者を職種でバブルソートする
  2. 上からA,B,C,A,B,C,A,...のようにグループに割り当てる(この例だと3グループできる)
  3. グループ内でシャッフルする(共有の順番をランダムにするため)
  participants.map(v => v["team"] = shainTeams[v["user"]]); // 職種を割り当てる
  participants.sort((a, b) =>
    ((a["team"] < b["team"]) ? -1 : ((a["team"] > b["team"]) ? 1 : 0)) // バブルソート
  );
  const nGroups = Math.ceil(participants.length / 6); // 最大6人のグループを作る
  let groups = new Array(nGroups).fill().map((_, i) =>
    shuffle(participants.filter((_, j) => (j + i) % nGroups === 0))
  );

特定の時刻に投稿する

GASのGUIで設定できる時間ベースのトリガーは1時間単位で分は選べない。GASの組み込みモジュール ScriptApp を使えば任意の時刻で実行できる。例えば当日の13:58に main 関数を実行するには、次の関数をトリガーに設定しておく。

function setTrigger() {
  let now = new Date();
  now.setHours(13);
  now.setMinutes(58);
  now.setSeconds(0);
  ScriptApp.newTrigger('main').timeBased().at(now).create();
}

おしまい

MNTSQはプロダクトだけでなく組織も育てがいのあるフェーズだ。様々な領域でエンジニアリング能力を発揮できるエンジニアを募集している。