Slackのemoji reaction数を集計するワンライナー(bash)

# 追記: 20200527
久々に叩いたら下記:
`bash: /usr/bin/find: Argument list too long`

ログが増えすぎたようで、頭の `find` を `find . -type f -name "*.json"` にすれば問題ないと思われる。たぶん。

# TL;DR
```sh
find */*.json | xargs cat | jq -r '.[].reactions | select(. != null) | .[] | "\(.name),\(.count)"' | xargs -I{} bash -c 'yes :$(echo "$1" | cut -d, -f1): | head -$(echo "$1" | cut -d, -f2)' - {} | sort | uniq -c | sort -nr | head -5
```

# はじめに
職場での日々のコミュニケーションは当然にSlackで行われているのであるが、それとは別にプライベートの友人たちと利用しているSlackチームがある。

日夜入り浸りニュース記事を貼って喧々囂々雑談してみたり、やれどの車がかっこいいだの、やれ次の旅先はどこそこにしようだの、そうかと思えば公安調査庁の資料を眺めてみたり、某銀の第三者委員会調査報告書を眺めてみたりする楽しいチャットである。

メンバーは20人弱、アクティブは15人を切る程度の規模にも拘わらず、登録されているcustom-emojiの数は700を超える。その殆どは [絵文字ジェネレーター](https://emoji-gen.ninja/) で粗製濫造したemojiである。エンジニアの集まりというわけではない。当然フリープランである。

# 事の発端
<img width="442" alt="ss 2018-09-08 at 10.48.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/24825/b05bb1c8-8a20-2506-49b3-1992aa3d3ef7.png">
色々の障りがありそうなので隠してみたが、blurし過ぎて何が何だか分からない。
要するにreactionに使われるemojiの上位を知りたいという話になったのである。

# ログの準備
調べてみるとフリープランでも、Public channelのログであれば過去に遡ってダウンロード可能らしい。Private channelも含めたログはエンタープライズプランのみ対応とのこと。

出力には管理者権限が必要らしいのだが、私はヒラのようだったので管理者に権限を要求した。
<img width="352" alt="ss 2018-09-08 at 11.00.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/24825/9641c535-d252-f75b-f6f0-d545df741e12.png">
いいスピード感である。

管理者用の設定画面を適当に触っているとそれらしい項目があったので、申請。
量が多い場合はダウンロードできるようになるまでかなり時間がかかることもあるようだが、今回は特にそのようなことはなさそうだった（少なくとも数分後にはダウンロードできた）。

# ログの中を見る
ダウンロードしたzipファイルをunzipすると、中は下記の構成になっていた。

```
┣━ Channel A
┃　　┣━ yyyy-MM-dd.json
┃　　┣━ yyyy-MM-dd.json
┃　　┣━ ...
〜中略〜
┣━ Channel Z
┃　　┣━ yyyy-MM-dd.json
┃　　┗━ yyyy-MM-dd.json
┣━ channels.json
┣━ integration_logs.json
┗━ users.json
```

チャンネル名以外のディレクトリは無いように見えるため、全ディレクトリに対して `yyyy-MM-dd.json` を走査していけばよさそうである。
`yyyy-MM-dd.json` の中身はこのようになっている。

```json
[
    {
        "type": "message",
        "user": "XXXXXXXXX",
        "text": "ほげほげ",
        "client_msg_id": "01234567-89ab-cdef-0123-456789abcdef",
        "ts": "1234567890.000000"
    },
    {
        ...
    },
    ...
]
```

reactionが付いた投稿はこうなる。

```json
{
    "type": "message",
    "user": "XXXXXXXXX",
    "text": "ほげほげ",
    "client_msg_id": "01234567-89ab-cdef-0123-456789abcdef",
    "ts": "1234567890.000000",
    "reactions": [
        {
            "name": "nanraka-no-emoji",
            "users": [
                "XXXXXXXXX",
                "YYYYYYYYY"
            ],
            "count": 2
        },
        {
            ...
        },
        ...
    ]
}
```

なるほど（？）
~~（特に書くことない）~~

# 方針
* シェルでなんとかなりそう
* jqは使いたい
* awkを使ったら負けた気がする
* セミコロンもなんか負けた気がする（forなどの制御構文）

と思いながらワンライナーに挑戦してみることにした。

# 結果（冒頭再掲）
こうなった（方針などと書いておきながら、過程については特に述べることがない）。

```sh
find */*.json | xargs cat | jq -r '.[].reactions | select(. != null) | .[] | "\(.name),\(.count)"' | xargs -I{} bash -c 'yes :$(echo "$1" | cut -d, -f1): | head -$(echo "$1" | cut -d, -f2)' - {} | sort | uniq -c | sort -nr | head -5
```

これをunzipしたルートで実行すると

```
458 :nanraka-no-emoji-1:
444 :nanraka-no-emoji-2:
387 :nanraka-no-emoji-3:
341 :nanraka-no-emoji-4:
335 :nanraka-no-emoji-5:
```

こういった具合で上位5件のemojiがreaction使用回数と共に出力される。なお実行時間はそこそこかかる。

`head -5` を `head -20` に変えれば上位20件が出力されるし、ファイルにリダイレクトして全件出力してみてもいい。 _It's up to you._

# 雑めの解説
## find
```sh
find */*.json
```
各ディレクトリ下にあるjsonファイル名を出力する

## xargs
```sh
xargs cat
```
前段の `find` の検索結果を受けて中身を出力

## jq
```sh
jq -r '.[].reactions | select(. != null) | .[] | "\(.name),\(.count)"'
```
jqはやや反則気味な気もするが、まともな環境なら大体入っているだろうということで使用。 `-r` は出力からクオーテーションを外すオプション。
見たままだが、reactionのないmessageもあるので `select(. != null)` で絞る。
`:nanraka-no-emoji:` によるreactionが3つ付いていたら

```
nanraka-no-emoji,3
```
と表示される。

## 再度のxargs
```sh
xargs -I{} bash -c 'yes :$(echo "$1" | cut -d, -f1): | head -$(echo "$1" | cut -d, -f2)' - {}
```
ここが一番気持ち悪い。もっと上手いやりようがありそうではある。
要するにやりたいことは、

```sh
nanraka-no-emoji,3
```
から

```sh
:nanraka-no-emoji:
:nanraka-no-emoji:
:nanraka-no-emoji:
```
への変換である。
ここで使っているのは `yes | head -n` によるn回ループ。また `{}` による位置指定はcommand substitution中では無効らしいので、 `bash -c` で代用した。

## sort | uniq | sort | head
```sh
sort | uniq -c | sort -nr | head -5
```
これは所謂集計とソートでよく見るやつなのでぐぐるとなんか出てくると思う（適当

# 所感
+ やっぱりパイプシステムつよい
  - 各コマンドの仕事は単純なのに繋げるとつよい（エモい）
  - 日々お世話になってます
+ xargsもつよい
  - 困ったときはxargsでどうにかなんないかな〜と思うとだいたいなんとかなる（エモい）
+ xargsの並列実行よく分かってない
  - `-P 4` とかすると並列にやってくれるけど、どういう制御をしてるのかよく分かってない
  - 4つ全部終わるまで待ってくれたりするのか、変な使い方をすると実行結果が変わりそう
  - 適当に使ってみたら確かに早くなったし実行結果も問題なかったけど