Slackの絵文字を全て取得する


Slackで使われている絵文字を`標準`、`カスタム`全て欲しかったのですが、割と面倒だったので記録しておきます

# やりたいこと

Slackのリアクション名から絵文字をHTMLで描画したい

例えば`:grin:`から😁を描画したい


方法として以下どっちかかなと思って調べました。

1. 画像として描画する
1. 絵文字として描画する

後述する理由により、カスタムアイコンは画像として描画する。組み込みアイコンは絵文字として描画する

という方法になりました。


## 画像として描画する


Slackには`emoji.list`というAPIが用意されているので、これを使えば`カスタムアイコン`の一覧がURLとして取得できます。

> 参考 [emoji.list method | Slack](https://api.slack.com/methods/emoji.list)

使い方は簡単で、以下のようにHTTPリクエストを投げるだけ

```bash
curl https://slack.com/api/emoji.list?token=<自分のアクセストークン>&pretty=1

```

こんな感じのJSONが返ってくるので、この情報から画像を取得すればOK

```json
{
    "ok": true,
    "emoji": {
        "name1": "https:\/\/emoji.slack-edge.com\/<teamID>\/name1\/f3d58f2bb0.png",
        "name2": "https:\/\/emoji.slack-edge.com\/<teamID>\/name2\/4650f4c0e0.png"
    }
}
```

ただし、`カスタムアイコン`しか取得できないので、最初から組み込まれているアイコンは習得できません。
組み込みで入っている絵文字も含めて欲しかったのですが、このAPIではとれないということで断念。

### Slackでは組み込みの画像をどうしてるか

ちなみに、Slackはどうしてるんだろう？と思って確認してみたらアイコン一つ分のhtmlは以下のようになってました。

```html
<span role="img" aria-label="woman lifting weights emoji"
 class="emoji-outer emoji-sizer"
 data-codepoints="1f3cb-fe0f-200d-2640-fe0f"
 style="background-image: url(&quot;https://cfr.slack-edge.com/c00d19/img/emoji_2017_12_06/sheet_apple_64_indexed_256.png&quot;); background-position: 19.6078% 90.1961%; background-size: 5200% 5200%;"
>:woman-lifting-weights:</span>
```

`https://cfr.slack-edge.com/c00d19/img/emoji_2017_12_06/sheet_apple_64_indexed_256.png`には全アイコンが敷き詰められた画像が置いてあって、それを座標で切り取って使ってるみたいです。
何度もダウンロードしないための処理っぽいですね

この方法を再現するのは流石に面倒ということで断念。

## 絵文字として描画する

Slack組み込みの絵文字はUnicode 11.0準拠の絵文字を採用しているということで、絵文字コードとして描画すれば画像なしでも問題なさそうです。

> [絵文字とエモティコンの使用 – Slack](https://get.slack.help/hc/ja/articles/202931348-%E7%B5%B5%E6%96%87%E5%AD%97%E3%81%A8%E3%82%A8%E3%83%A2%E3%83%86%E3%82%A3%E3%82%B3%E3%83%B3%E3%81%AE%E4%BD%BF%E7%94%A8)
> Slack では、標準の絵文字コードを採用しています。絵文字コードを入力すると、絵文字を手早くメッセージに追加できます！

ということなので、絵文字の名前と絵文字コードを対応付けできればOKですね
で、その対応表どこかにないかなと探しました。

[How can I get the FULL list of slack emoji through API? - Stack Overflow](https://stackoverflow.com/questions/39490865/how-can-i-get-the-full-list-of-slack-emoji-through-api) で紹介されていた以下のリポジトリを利用させていただきました。

[iamcal/emoji-data: Easy to parse data and spritesheets for emoji](https://github.com/iamcal/emoji-data)

このリポジトリには絵文字データと画像の情報をまとめくれています。
画像も用意されているので、自分で用意すれば画像としての描画もできましたが今回は遠慮しておきました。

### 使い方


```bash
npm install emoji-datasource
```

とするとインストールできるので、Node.jsで読み込んで使えます。

```javascript
const emojiData = require('emoji-datasource')
```

emojiDataは以下のような絵文字データの配列になってます。

```json
[
    {
        "name": "WHITE UP POINTING INDEX",
        "unified": "261D-FE0F",
        "non_qualified": "261D",
        "docomo": null,
        "au": "E4F6"
        ...
    },
    {
        "name": "WHITE UP POINTING INDEX",
        "unified": "261D-FE0F",
        "non_qualified": "261D",
        "docomo": null,
        "au": "E4F6"        
        ...
    }
    ...
]
```

情報はいっぱいあったんですが、`short_name`が絵文字名、`unified`が文字コードとなってそうだったのでその２つだけ使いました。

### とりあえず描画

😁の情報を取ってみると

```javascript
{ name: 'GRINNING FACE WITH SMILING EYES',
  unified: '1F601',
  non_qualified: null,
  docomo: 'E753',
  au: 'EB80',
  softbank: 'E404',
  google: 'FE333',
  image: '1f601.png',
  sheet_x: 30,
  sheet_y: 25,
  short_name: 'grin',
  short_names: [ 'grin' ],
  text: null,
  texts: null,
  category: 'Smileys & People',
  sort_order: 2,
  added_in: '6.0',
  has_img_apple: true,
  has_img_google: true,
  has_img_twitter: true,
  has_img_facebook: true,
  has_img_messenger: true }
```

こんな情報がとれたので、`unified`に文字参照を付けて

```test.html
<div>&#x1F601;:grin</div>
```

![grin](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/330514/bcde77ca-e32f-90e2-d3ee-ebca2bce066f.png)

やったぜ😁

### マルチバイト文字で問題発生

`grin`の場合は`1F601`というシンプルなものだったのでよかったんですが、例えば🙋‍♀️だと以下のように長いです。

```json
{
    "short_name": "woman-raising-hand",
    "unified": "1F64B-200D-2640-FE0F"
}
```

同じ方法で行くと

![alt](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/330514/75ef5638-a11c-bbf6-7ae7-38f02d220888.png)


残念

まあ複数バイトで表しているので当然か


ということで、`-`を文字参照に置換しました

```javascript
//@ts-check
const emojiData = require('emoji-datasource')

const dic = {}
emojiData.forEach(emoji => {
    dic[emoji.short_name] = `&#x${emoji.unified};`.replace(/-/g, ";&#x")
})
console.log(JSON.stringify(dic, null, 2))
```

こんな感じにすると、キーが絵文字名でバリューが文字コードの対応表が作成できます。
ひとまずこれで十分かなと

[Gist](https://gist.github.com/dbgso/fb12233fc6360393b08e3e96cedfbf5b)に上げといたので、必要となったらどうぞ

### 確認

確認用にhtmlとして吐き出してみましたが大丈夫そうでした。

```html
...
<div>&#x1F641;:slightly_frowning_face</div>
<div>&#x1F642;:slightly_smiling_face</div>
<div>&#x1F643;:upside_down_face</div>
<div>&#x1F644;:face_with_rolling_eyes</div>
<div>&#x1F645;&#x200D;&#x2640;&#xFE0F;:woman-gesturing-no</div>
<div>&#x1F645;&#x200D;&#x2642;&#xFE0F;:man-gesturing-no</div>
<div>&#x1F645;:no_good</div>
<div>&#x1F646;&#x200D;&#x2640;&#xFE0F;:woman-gesturing-ok</div>
<div>&#x1F646;&#x200D;&#x2642;&#xFE0F;:man-gesturing-ok</div>
<div>&#x1F646;:ok_woman</div>
<div>&#x1F647;&#x200D;&#x2640;&#xFE0F;:woman-bowing</div>
<div>&#x1F647;&#x200D;&#x2642;&#xFE0F;:man-bowing</div>
<div>&#x1F647;:bow</div>
<div>&#x1F648;:see_no_evil</div>
<div>&#x1F649;:hear_no_evil</div>
<div>&#x1F64A;:speak_no_evil</div>
<div>&#x1F64B;&#x200D;&#x2640;&#xFE0F;:woman-raising-hand</div>
<div>&#x1F64B;&#x200D;&#x2642;&#xFE0F;:man-raising-hand</div>
<div>&#x1F64B;:raising_hand</div>
<div>&#x1F64C;:raised_hands</div>
...
```

![alt](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/330514/bf6d6e14-9d0f-5234-8ce4-64ba9656e687.png)


# 備考

## スペース注意

文字の間にスペースがあると別々の文字という風に判定されます。

```html
<div>&#x1F64B;&#x200D;&#x2642;&#xFE0F;:man-raising-hand</div>
<div>&#x1F64B; &#x200D; &#x2642; &#xFE0F; :man-raising-hand</div>
```

![alt](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/330514/c0fc4c4d-a64d-6755-6374-e98c9157a335.png)

ラインとかで見るやつ

## アイコン似すぎ問題

![alt](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/330514/d63060ab-41fa-b14e-9a16-4fd1465514d3.png)

変換に漏れがあるのかと思ったら単純にアイコンが似すぎてて草