slackAPIを使って各ユーザー・チャンネルごとの絵文字使用回数ランキングを出す

### TL;DR (Too Long, Didn't Read)

slackで使ってる絵文字の使用回数を各ユーザー・チャンネルごとにランキング化してくれるスクリプトを`ruby`で書きました。
[リポジトリはこちら](https://github.com/H-Asakawa/slack_emoji_servey/blob/master/README.md)
slackAppを作成してtokenを取得するだけなので、ぜひ使ってみてください。

---

### はじめに
- 仕事でslackを使っていたとき、チーム内でオッケー🆗という肯定の返事をする際に、男性→女性の場合は女性ok🙆‍♀️、女性→男性の場合には男性ok🙆‍♂️という記号が使われる傾向があるのに気づきました（ちなみにチーム内の男女比はほぼ均等）。

---

- オッケー🆗しているのは本人なのに、相手側の性別で🙆‍♀️🙆‍♂️を返すという文化が不思議でした（そして自分もなんとなくその慣習に従っています）。
この使い分けは特にマナーとして定義はされていません。

---
- 相手に対する気遣いか、単になんとなく（無意識）か、いずれにせよslackで使っている絵文字の記号選定には、その人の個性だったり、あるいは無意識だったりが関係していると思います。
そこで、どんな傾向があるのかをちょっと調べてみたくなりました。

---
- とはいえ、別に細かく統計的なデータを出して全部分析したいとかじゃ全然無いです（研究かよ..）。
普段なんの絵文字を使ってるんだろう、このチャンネルって普段どの絵文字が流行ってるの？とかをチャチャっと出せないものか調べてみました。

---
- その結果、2016年〜2019年にかけて自分と同様にslackでの絵文字コミュニケーション文化に関心を持った先人達がいたので、参考にしながら自分もやってみることにしました。

---

### やりたいこと

- 絵文字の使用率ランキングが見たい
- チャンネルごとに知りたい
- ユーザーごとに知りたい

---

### 実装方針

1. ユーザー名かチャンネル名を入力
- 対象ユーザーのreactions（反応データ）を取得
- 対象チャンネルのreactions（反応データ）を取得
- reactionsから絵文字と使用回数を抽出してランキングをチャンネルに投稿

（ランキング化と投稿は別々に分けたほうがよかったですね。）

---

### 実装
[リポジトリはこちら](https://github.com/H-Asakawa/slack_emoji_servey/blob/master/README.md)
使い方や構築手順などは、READMEに詳しく書いたのでぜひ見てください。

コードを一部のみ紹介します。

---

#### 1. user名かchannel名を入力

標準入力から値を受け取ってuser名かchannel名かを判別しました。
BOT化するときは、チャンネルを監視しといて投稿された文字列をここに入れるとよさそうです。

---

~~~ruby
p '調べたいのはどっち？入力してね　user or channel'
target = gets.chomp!

# reactionsを取得
if target == "user"
  reactions = get_reactions_from_user
elsif target == "channel"
  reactions = get_reactions_from_channel
else
  puts "user or channelのどっちかを入力してください"
  return
end

post_emoji_ranking(reactions, target)
~~~

---

#### 2. userからreactions(反応データ)を取得
reactions（反応データ）を取得するために、slackAPImethodの[公式リファレンス](https://api.slack.com/methods)からよさげなメソッドを探します。
次に、slackAppを新規作成して該当する`permisson`を付与したあと、tokenの値を取得します。

---

今回は下記の`permission`を付与しました
- `channels:history`：チャンネルの履歴データを取得
- `channels:read`：チャンネル名とチャンネルIDを取得
- `chat:write`：チャンネルに投稿
- `emoji:read`：カスタム絵文字を読み取る
- `reactions:read`：反応データを取得
- `users:read`：ユーザー名とユーザーIDを取得

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528819/e0e64603-a643-e40e-1f44-176270b38ca9.png)

---

ユーザーの行った絵文字による反応は、slackAPIの`reactions.list`を使うと取得できます。
user_idをクエリパラメータとして渡すことで、該当userのreactions（反応）のjsonデータが取得できます。
このデータの中に、emoji（絵文字）とcount（数）とusers(押した人)の値が入ってます。
（なお、slackAPI制限のため1000レコードまでしか取得できません。）

---

##### 2.1. 標準入力で受け取ったuser名をuserIDに変換する処理

~~~ruby
p '絵文字使用率を調べたいユーザー名を入力してください。'
  $target_name = gets.chomp!

# SlackAPI：users.list
  res     = Net::HTTP.get(URI.parse("https://slack.com/api/users.list?token=#{SLACK_API_TOKEN}&pretty=1"))
  hash    = JSON.parse(res)
  members = hash["members"]

# ユーザー名を知ってればすぐ調べられるようにuserIDとuser名のハッシュを作っておく
  member_lists = {}

  members.each do |member|
    member_lists[member["name"].to_sym] = member["id"]
  end

  if member_lists[$target_name.to_sym].nil?
    puts "#{$target_name}は存在しません"
    return
  end

# 該当ユーザーのIDを取得する
  user_id = member_lists[$target_name.to_sym]
~~~

---

##### 2.2. user_idを使って該当userのreactionsを取得する処理

~~~ruby
# SlackAPI：reaction.list
  res = Net::HTTP.get(URI.parse("https://slack.com/api/reactions.list?token=#{SLACK_API_TOKEN}&count=#{COUNT}&user=#{user_id}&pretty=1"))

  hash  = JSON.parse(res)
  items = hash["items"]

  messages = []
  items.each do |item|
    messages << item["message"]
  end

  reactions = []
  messages.each do |message|
    reactions << message["reactions"]
  end
  reactions.flatten
~~~

---

#### 3. 対象チャンネルからreactions（反応データ）を取得
チャンネル内で行われた絵文字による反応は、slackAPIの`channels.history`を使って取得してきます。
`channel_id`をクエリに渡すことで、該当channelの履歴データが取得できます。
そこから投稿メッセージ(messages)を抜き出して、その中のreactions（反応）のjsonデータを取得します。
（なお、slackAPIの制限のため投稿は1000レコードまでしか取得できません。）
（latest,oldestなどtimestampを指定すれば1000件ずつ取得できるようなので、取得期間をずらして複数回リクエストすれば全件取得できなくもないかもです..）

---

##### 3.1. 標準入力で受け取ったchannel名をchannel_idに変換する処理

~~~ruby
p '絵文字使用率を調べたいチャンネル名を入力してください。'
  $target_name = gets.chomp!

# チャンネルリスト取得
  res      = Net::HTTP.get(URI.parse("https://slack.com/api/channels.list?token=#{SLACK_API_TOKEN}"))
  hash     = JSON.parse(res)
  channels = hash["channels"]

# チャンネル名だけ知ってればすぐ調べられるように、チャンネルIDとチャンネル名のハッシュを作っておく
  channel_lists = {}
  messages      = []

  channels.each do |channel|
    channel_lists[channel["name"].to_sym] = channel["id"]
    messages << channel["messages"]
  end

  if channel_lists[$target_name.to_sym].nil?
    puts "#{$target_name}は存在しません"
    return
  end

# 該当チャンネルのIDを取得する
  channel_id = channel_lists[$target_name.to_sym]
~~~

---

##### 3.2. channelIDを使って該当userのreactionsを取得する処理

~~~ruby
# SlackAPI：channels.history
  res = Net::HTTP.get(URI.parse("https://slack.com/api/channels.history?inclusive=true&count=#{COUNT}&channel=#{channel_id}&token=#{SLACK_API_TOKEN}"))
  hash      = JSON.parse(res)
  messages  = hash["messages"]
  reactions = []

  messages.each do |message|
    reactions << message["reactions"]
  end

# 整形
  reactions.compact.flatten
~~~

---

#### 4. reactionsから絵文字と使用回数をランキング化してチャンネルに投稿
対象ユーザーorチャンネルのreactions（反応データ）が取得できました。
これを使ってemojiとcount数の良い感じのhashを作り、降順にソートしてランキング化します。

---


そのデータをslackAPIの`chat.post`に渡してチャンネルに投稿します。
絵文字と使用回数のデータは`attachments`を使って表示しようかなぁと思ったんですが、うまく動かなかったんでやめました。
改行できれば十分なことに気づいたので改行文字列（\n）をつけて保存し、全部まとめてcontentカラムに突っ込んでます。
絵文字(emoji)は、`:emoji:`のようにコロンで囲ってあげるとslackに投稿した時に文字列がアイコン化されます。

---

##### 4.1. 投稿用のpostリクエストはnet/httpを使って実装

gemの用意

~~~ruby
require 'net/http'
require 'uri'
~~~

---

~~~ruby
def post_emoji_ranking(reactions, target_type)
#取得したreactionsをランキング化して投稿
# あとでcount数を加算するために初期値0でハッシュを作っておく
  results = Hash.new(0)

  #ユーザーを対象にした場合はcountは使用せず単に1プラスする。チャンネルを対象にした場合はcountを加算する。
  if target_type == "user"
    reactions.each do |reaction|
      name                 = reaction["name"]
      results[name.to_sym] += 1
    end
  else
    reactions.each do |reaction|
      name                 = reaction["name"]
      results[name.to_sym] += reaction["count"]
    end
  end

  # コンソール結果表示用
  puts "#{$target_name}の絵文字使用率ランキング1〜10位"
  result_data = []
  results.sort_by { |_, v| -v }.to_a.first(10).each do |result|
    result_data << result
    puts "#{result[0].to_s.rjust(30, " ")}:#{result[1]}回"
  end

  # 該当チャンネルに投稿をするAPIを叩く
  post_api_url = "https://slack.com/api/chat.postMessage"

  uri = URI.parse(post_api_url)
  req = Net::HTTP::Post.new(uri)

  # 後でjoinして配列内の文字列を全て結合する
  contents = ["#{$target_name}の絵文字使用率ランキング1〜10位\n"]
  result_data.each.with_index(1) do |data, n|
    contents << "#{n}位　:#{data[0]}:は#{data[1]}回です\n"
  end

  post_data = { token:   "#{SLACK_API_TOKEN}",
                channel: "#{POST_CHANNEL_NAME}",
                text:    contents.join, }

  req.set_form_data(post_data)

  req_options = {
    use_ssl: uri.scheme == "https"
  }

  response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
    http.request(req)
  end
end
~~~

---

##### 4.2. 環境が変わっても使えるように`dotenv`を導入

.envファイルを作成

```
SLACK_API_TOKEN   = "your_token"
POST_CHANNEL_NAME = "post_channel_name"
```

---

.envを読み込む

~~~ruby
require 'dotenv'
Dotenv.load

SLACK_API_TOKEN   = ENV['SLACK_API_TOKEN'] # slackAPI用に取得したtoken
POST_CHANNEL_NAME = ENV['POST_CHANNEL_NAME'] # 通知対象チャンネル名
~~~

---

### 結果

こちらは筆者の絵文字使用率ランキング1~10位です。
即レスでリアクションを返してるためか、デフォルトの絵文字データセットの中から無難で使いやすいものばかりを選択しています。
その傾向をはっきり確認することができました（なんてつまらない奴...）。
あと、最初に言ったとおり謎の慣習にとらわれているせいか🙆‍♀️の使用率が高いですね。

---
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528819/161ae2aa-51c7-c90e-8487-6d2ed7f141a5.png)

---

こちらは筆者が仕事で使ってる分報チャンネル（分報とは、作業ログやつぶやきなどを垂れ流すことで、タスクの見える化、早期の知見共有、助け合いを目指したもの。が、一見仕事に関係なさそうな話題もバンバン投下してる。）における絵文字使用率ランキング１~10位です。

---

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528819/1487ae91-1c35-2464-2d5d-19dd6d4495b9.png)

---

##### １位：eys（目）
明らかにeye、目が多い。つまり、既読ということですね..。
目はlineの既読機能を代替してくれる大変有能な絵文字記号だと思います。
筆者の投稿は、周囲にとって「お、おう...」となるものが多いために目が多く使われるのかなと思いました。
見てもらえているだけ感謝しています。

---

##### 2位：+1（親指）、3位:wakaru（わかる）、4位：naruhodo（なるほど）

次に、親指、わかる、なるほど等の納得系の絵文字使用回数が多いです。
これは、筆者が困ったことやわからないことがあったときに、同僚や先輩からの返信やリンク共有などの情報提供に対して、筆者自身が返している絵文字じゃないかと思います。
分報が機能しているといえるので、これは朗報です。

---

##### 5位：heart（ハート）
筆者はハート絵文字に複雑な評価を抱いてるので、これを使用することはあり得ないです。
よって、これは自分の投稿に対して誰かがつけたものです。
小さなチャンネルなのですぐにわかってしまうんですが、犯人は筆者と仲の良い同僚の男性です。
（そういえば、qiitaのいいねボタンはチェックマークなの良いですね。→とおもいきやLGTMに変更となった。それもまたヨシ）

---

##### ちなみにconsoleのログ出力はこんな感じ

```
"調べたいのはどっち？入力してね　user or channel"
channel
"絵文字使用率を調べたいチャンネル名を入力してください。"
_asakawa
_asakawaの絵文字使用率ランキング1〜10位
                          eyes:28回
                            +1:10回
                        wakaru:6回
                      naruhodo:6回
                         heart:6回
               congratulations:5回
                          kusa:5回
 rolling_on_the_floor_laughing:5回
                      innocent:4回
                    heart_eyes:4回
```

---

### 考察

- 複数の大きめのチャンネルを対象に結果を表示してみると、チームごとのちょっとした文化の違いが確認できました（例えば、お祝い絵文字（祝:congratulations）が多いチームと、だれかを労る絵文字(お大事に:odaijini2)が多いチームなど）。

---

- slack導入企業では業務コミュニケーションのほとんどがslackでのやりとりだと思うので、そこで行われているやりとりのダイナミズムを分析することはHR的な職場改善やメンタルヘルスケア等にも応用出来るかもと思います（筆者はこの路線にはあまり関心はない）。

---

- 一方で、絵文字の使用状況がチームのカルチャー（コミュニケーション文化）の違いを示しているなら、逆説的には使用する絵文字の種類をコントロールすることでカルチャーを意図的に生成したり育てることもできるといえそうです（筆者はこの路線には少し関心がある）。

---

- その延長でさらに考えると、slack側がデフォルトで用意している絵文字のデータセットだけを使っていたら、カルチャーの育成に限界を生んでしまっているのかもしれません。
- コミュニケーションコストの最小化だけを目的にするのなら、使用する絵文字のデータセットは最小限のほうが良いです。

---

- しかし、絵文字は自分の表明したかった気持ちを記号に簡略化してしまうので、誰かの意見に対して本当は多様な反応を持っていたはずなのに、いつの間にかそれが画一化してしまっている危険性もあります。
- カスタム絵文字はそれらに抵抗するために存在しているのかもしれません。

---

- これらを踏まえてコミュニケーションコストの最小化とカルチャーの育成には、トレードオフの関係があると思いました。

---

### 所感
- うーん、最初にやりたかったことは実現できたけど、いざ結果を出して眺めてみると期待してたほど面白い結果は出ませんでした（何の成果も得られませんでしたぁぁぁッッ！）。
- このスクリプトは毎回slackAPIを叩きにいくので、ルーティンワーク的にちょこっと分析に使えるのが利点かなと思います。

---

- なのでBOT化して月ごとに集計してチャンネルに通知するのが現実的な運用かなと思いました。
- とりあえず、もう少しカスタム絵文字を使ったほうがよさそうです。
- これを使ってみた人は、ぜひ感想コメントなどお待ちしています。

---

### 参考
・[slackAPImethod公式リファレンス](https://api.slack.com/methods) 
APIの仕様とかはここに全部載ってます。
・[社内で使われているSlackの人気絵文字を調査してみた](https://techblog.securesky-tech.com/entry/2019/01/08/)
筆者のやりたかったことはこの人がほぼ実現していたのでおおいに参考にさせてもらいました。使用言語はpythonです。

---

・[社内Slackの絵文字事情を調査する Part 1. 下準備編](https://www.flywheel.jp/blog/fw-slack-investigation-part1/)
この人は全slack履歴データを落としたあとrubyで整形したのち、pythonを使って統計分析してます。筆者のできなかった大規模データの統計分析からコミュニケーション文化の考察を試みています。
・[社内Slackでどんな絵文字リアクションがよく使われているかをGoで集計してみた](https://qiita.com/dondoko-susumu/items/73d83ace88c9b507a371)
この人はGOでスクリプトを書いてます。実装方針を考えてるときに参考にさせてもらいました。

---

・[TL;DRの意味を勘違いして使っていたら顰蹙を買ったので気をつけて使おう！](https://qiita.com/kei_0121/items/2e2e070702a82a79de8e)
筆者は初見でTL;DRの意味がよくわからずちょっと困惑した経験があるので（セミコロンがちょっとね..）、省略前の英文も付与しました。TL;DR自体はユーザーフレンドリーでとても良いと思うし、nerd（おたく）な表現とのことなので採用しました。

---

・[15,000個のカスタム絵文字を支える、Slack絵文字登録術]
(https://qiita.com/hartmann16325/items/0788b898fae529b1876d#%E7%B5%B5%E6%96%87%E5%AD%97%E3%81%8C%E5%A2%97%E3%81%88%E3%81%9F%E3%81%93%E3%81%A8%E3%81%AB%E3%82%88%E3%82%8B%E8%89%AF%E3%81%84%E5%8A%B9%E6%9E%9C)
カスタム絵文字を用いたコミュニケーションの利点に関する説明は、納得できる部分が多かったです。
・[Markdown記法 サンプル集](https://qiita.com/tbpgr/items/989c6badefff69377da7#-code%E8%A8%98%E6%B3%95)
めちゃ見やすくて助かりました。