Python
emoji
Slack

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

More than 1 year has passed since last update.

社内飲み会用のネタです :wink: :beers:

私の所属チームでよく使われているSlackのEmoji reactionsを集計しました。


結果

よく使われているSlack Emoji reaction上位7つです。

※数値は使用された回数

スクリーンショット 2016-09-16 17.38.40.png

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


集計手順


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

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 で実行しました。


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!')



実行と確認

$ 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回数を集計します。


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)



実行と確認

$ 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にコピペしてしまいましょう。