私のチームでよく使われているSlack Emoji Reactions

社内飲み会用のネタです :wink: :beers:
私の所属チームでよく使われているSlackのEmoji reactionsを集計しました。

# 結果

よく使われているSlack Emoji reaction上位7つです。
*※数値は使用された回数*

![スクリーンショット 2016-09-16 17.38.40.png](https://qiita-image-store.s3.amazonaws.com/0/79127/0359e04b-b048-a7d6-1018-5ec0ddf98efe.png)

というわけで、最も使われているのは :ok_woman: (`:ok_woman:`) でした。「了解」のつもりでよく使われますね。 :bow: （`:bow:`）も「よろしくお願いします」という感覚で使われやすい。

# 集計手順

## Slackからチームのログをダウンロードする

* ドキュメント
  * https://get.slack.help/hc/en-us/articles/201658943-Export-your-team-s-Slack-history

OwnerかAdminの権限が必要です。
ダウンロードしたファイルを解凍すると次のようになっています。

------
* :page_facing_up: users.json
* :page_facing_up: channels.json
* :page_facing_up: integration_logs.json
* :file_folder: general
  * :page_facing_up: 2015-08-03.json
  * :page_facing_up: 2015-08-04.json
  * ...
* :file_folder: random
  * :page_facing_up: 2015-08-03.json
  * :page_facing_up: 2015-08-04.json
  * ...
* ...

------

チャネルごとディレクトリになっており、日別のファイルにJSON形式で格納されています。

## ログをtsv形式に変換 

reactionssだけ数えるならJSONから直接集計もできそうですが、汎用的に分析しやすいようtsvに変換しておきます。今回のスクリプトは `Python 3.5.1` で実行しました。

```py3:convert_to_tsv.py
# -*- coding: utf-8 -*-
# ExportしたSlackの履歴ファイルからメッセージのログをtsvに出力するスクリプト

import json
import os
import shutil
import sys
import zipfile


class SlackLogs(object):

    def __init__(self, filepath):
        self.__cwd = os.getcwd()
        self.__tmp_dir = '__tmp'
        self.__unzip(filepath)
        self.__users = self.__fetch_users()
        self.__channels = self.__fetch_channels()

    def __unzip(self, filepath):
        self.delete_tmp_dir()
        os.mkdir(self.__tmp_dir)
        with zipfile.ZipFile(filepath, 'r') as zf:
            zf.extractall(path=self.__tmp_dir)

    def __fetch_users(self):
        users = {}

        u_fp = os.path.join(self.__tmp_dir, 'users.json')
        with open(u_fp, 'r') as f:
            data = json.load(f)
        for u in data:
            users[u['id']] = u['name']
        return users

    def __fetch_channels(self):
        fp = os.path.join(self.__tmp_dir, 'channels.json')
        with open(fp, 'r') as f:
            data = json.load(f)
        return [c['name'] for c in data]

    def __map_to_user_name(self, user_id):
        if user_id in self.__users:
            return self.__users[user_id]
        return user_id

    def __arrange_message(self, message, channel):
        def optional_user(m):
            if 'user' not in m:
                return ''
            return self.__map_to_user_name(m['user'])

        def optional_text(m):
            if 'text' not in m:
                return ''
            return m['text']

        def arrange_reactions(m):
            if 'reactions' not in m:
                return []
            a = []
            for r in m['reactions']:
                for u in r['users']:
                    a.append({'name': r['name'], 'user': self.__map_to_user_name(u)})
            return a

        return {
            'channel':   channel,
            'ts':        message['ts'],
            'type':      message['type'],
            'user':      optional_user(message),
            'text':      optional_text(message),
            'reactions': arrange_reactions(message)
        }

    def __fetch_messages(self):
        def fetch_messages_from_a_channel(channel):
            files = os.listdir(os.path.join(self.__tmp_dir, channel))
            messages = []
            for file in files:
                f_path = os.path.join(self.__cwd, self.__tmp_dir, channel, file)
                with open(f_path, 'r') as f:
                    data = json.load(f)
                    messages.extend([self.__arrange_message(m, channel) for m in data])
            return messages

        messages = []
        for c in self.__channels:
            messages.extend(fetch_messages_from_a_channel(c))

        messages.sort(key=lambda x: x['ts'])
        return messages

    def output_to_tsv(self):
        messages = self.__fetch_messages()
        f_path = os.path.join(self.__cwd, 'slack_logs.tsv')
        with open(f_path, 'w') as f:
            for m in messages:
                f.write('\t'.join([
                    m['channel'],
                    m['ts'],
                    m['type'],
                    m['user'],
                    m['text'].replace('\t', '\\t').replace('\n', '\\n').replace('\r', '\\r'),
                    json.dumps(m['reactions'])
                ]))
                f.write('\n')
        return

    def delete_tmp_dir(self):
        if os.path.exists(self.__tmp_dir):
            shutil.rmtree(self.__tmp_dir)

if __name__ == '__main__':
    filepath = os.path.join(os.getcwd(), sys.argv[1])
    if not os.path.exists(filepath):
        print('Not found {}'.format(filepath))
        exit()
    slack_logs = SlackLogs(filepath)
    slack_logs.output_to_tsv()
    slack_logs.delete_tmp_dir()
    print('Finised!')
```

```bash:実行と確認
$ ls
convert_to_tsv.py  Exportしたファイル.zip
$ python convert_to_tsv.py Exported_Files_from_Slack.zip
Finished!
$ ls
convert_to_tsv.py  Exportしたファイル.zip  slack_logs.tsv
```



# Emojiごとにカウントする

次のスクリプトで全期間、全チャネル、全ユーザーにおけるEmojiごとのreaction回数を集計します。

```py3:count_emoji_reactions.py
# -*- coding: utf-8 -*-
# tsv形式のSlackログ履歴から集計する

import json
import os
import shutil
import sys
import zipfile
import re
import datetime


def count_emoji_reactions(logs):
    reactions = {}
    for m in logs:
        for r in m['reactions']:
            reactions.setdefault(r['name'], 0)
            reactions[r['name']] += 1

    for k, v in sorted(reactions.items(), key=lambda x: -x[1]):
        print(':{}: : {}'.format(k, v))

    return

def read_tsv(filepath):
    def format_log(line):
        log = line.rstrip().split('\t')
        try:
            return {
                'channel':   log[0],
                'ts':        float(log[1]),
                'type':      log[2],
                'user':      log[3],
                'text':      log[4],
                'reactions': json.loads(log[5])
            }
        except:
            print(log)
            exit()

    with open(filepath, 'r') as f:
        return [format_log(l) for l in f]

if __name__ == '__main__':
    filepath = os.path.join(os.getcwd(), sys.argv[1])
    if not os.path.exists(filepath):
        print('Not found {}'.format(filepath))
        exit()
    logs = read_tsv(filepath)
    count_emoji_reactions(logs)
```

```bash:実行と確認
$ ls
convert_to_tsv.py  count_emoji_reactions.py  Exportしたファイル.zip  slack_logs.tsv
$ python count_emoji_reactions.py slack_logs.tsv
:ok_woman: : 942
:bow: : 645
:+1: : 597
:raising_hand: : 465
:clap: : 298
:sushi_thanks: : 260
:pray: : 169
:sushi_kirakira: : 141
:tada: : 93
:sushi_yeah: : 92
:sushi_surprised: : 69
:innocent: : 67
:thinking_face: : 62
:meat_on_bone: : 58
:grinning: : 54
:eyes: : 50
:sushi_yes: : 43
:ok_hand: : 42
:smiley: : 42
:beastie: : 32
...
```

このままでは味気ないので結果をSlackにコピペしてしまいましょう。