kokh log

yumikokhの開発日記

テキスト:emoji:をUnicode絵文字👍に変換する

以前紹介した、外部サービスの投稿をNotion日報へ連携するカスタムAPIの続きです。

kokh.hateblo.jp

ZappierでSlackに投稿されたポストをwebhookで受取り、カスタムAPIへのリクエストを通してNotion日報に追加するようにしています。
ここで、webhookで受け取るテキストはこのようになっています。

Airpods proが不調 :thinking_face:

このままNotionに追加しても、 :thinking_face: はUnicode絵文字に変換されません。
そのため、カスタムAPI側で変換を行う処理を追加しました。

Slackのdocumentにはこう書いてあります。

If you're retrieving messages, you'll receive the emojis in colon format, so you might want to convert them back to their unicode emoji form. The compatible emoji formats are the Unicode Unified format (used by OSX 10.7+ and iOS 6+), the Softbank format (used by iOS 5) and the Google format (used by some Android devices). These will be converted into their colon-format equivalents.

メッセージを取得する場合、コロン形式の絵文字を受け取るので、Unicode 絵文字形式に戻す必要があるかもしれません。互換性のある絵文字形式は、Unicode 統合形式 (OSX 10.7 および iOS 6 で使用)、Softbank 形式 (iOS 5 で使用)、および Google 形式 (一部の Androidバイスで使用) です。これらは、コロン形式の同等の形式に変換されます。

Formatting text for app surfaces | Slack

emoji-dataをまとめてくれているパッケージがあるのでそちらを使います。

www.npmjs.com

packageの中身はこのようなテキスト絵文字とUnicode絵文字を対応させるjsonになっています。
packageサイズが28.5Mと大きめなので少し心配でしたが、問題ありませんでした。

[
    {
        "name": "WHITE UP POINTING INDEX",
        "unified": "261D-FE0F",
        "non_qualified": "261D",
        "docomo": null,
        "au": "E4F6",
        "softbank": "E00F",
        "google": "FEB98",
        "image": "261d.png",
        "sheet_x": 1,
        "sheet_y": 2,
        "short_name": "point_up",
        "short_names": [
            "point_up"
        ],
        "text": null,
        "texts": null,
        "category": "People & Body",
        "subcategory": "hand-single-finger",
        "sort_order": 170,
        "added_in": "1.4",
        "has_img_apple": true,
        "has_img_google": true,
        "has_img_twitter": true,
        "has_img_facebook": false,
        "skin_variations": {
            "1F3FB": {
                "unified": "261D-1F3FB",
                "image": "261d-1f3fb.png",
                "sheet_x": 1,
                "sheet_y": 3,
                "added_in": "6.0",
                "has_img_apple": true,
                "has_img_google": false,
                "has_img_twitter": false,
                "has_img_facebook": false,
            }
            ...
            "1F3FB-1F3FC": {
                ...
            }
        },
        "obsoletes": "ABCD-1234",
        "obsoleted_by": "5678-90EF"
    },
    ...
]

実装で : に囲まれた英数字を正規表現で探索し、マッチしたらUTF-16で変換します。

import emoji from "emoji-datasource";

const text = "Airpods proが不調 :thinking_face:";

// emojiを変換
const emojiRegex = /:([a-z0-9_+]+):/g;
const textWithEmoji = text.replace(emojiRegex, (match, p1) => {
  const emojiData = emoji.find((e) => e.short_name === p1);
  if (emojiData) {
    return String.fromCodePoint(parseInt(emojiData.unified, 16));
  }
  return match; // 見つからない場合は :emoji: のまま返す
});
  • parseInt は、文字列を整数に変換する関数です。この場合、emojiData.unified が16進数(16)で表されているため、parseInt に 16 を渡して16進数を10進数に変換します。
  • String.fromCodePoint() は、指定されたコードポイント(10進数のUnicode)を文字として変換します

これで、 :thinking_face: が 🤔 として出力されました 🎉

余談

あまり深く調べてませんが、Unicode emojiの成り立ちやデザインガイドラインがおもしろそうでした

unicode.org

zenn.dev