3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

イケてる絵文字に飢えている貴方のための Slackカスタム絵文字 [Emoji Kitchen][絵文字キッチン]

Posted at

おまけが本編です
nyan_bowing.png

本記事の概要

こんな感じのイケてる絵文字がSlackに欲しいなあ~と思ったら、本記事を読もう!
→ Emoji Kitchen

本記事ではGitHub上に公開されているEmoji Kitchenのサンプルコードをローカルで実行し、これをSeleniumで操作して絵文字を組み合わせた画像ファイルをダウンロードし、Slackで検索できるようファイル名を絵文字の名前に変更します。その後、その画像ファイルを手作業でSlackワークスペースに登録します。

ソースコード

emoji_kitchen.py
import time

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.action_chains import ActionChains
from webdriver_manager.chrome import ChromeDriverManager
import requests

service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

# レスポンシブするまで画面の横幅を狭める
driver.set_window_size(650, 768)

SAVE_DIRECTORY=r'./emoji_kitchen'

driver.get("http://localhost:5173")

driver.implicitly_wait(15)

actions = ActionChains(driver)

# フローティングボタンのせいでボタンが押せないよエラーが出るので、
# フローティングボタンを消す
floatingButton = driver.find_element(By.CSS_SELECTOR, "#root > div > div.MuiContainer-root.MuiContainer-maxWidthXl.css-15f64v6-MuiContainer-root > div > button")
driver.execute_script("arguments[0].style.display='none';", floatingButton)

button1 = driver.find_element(By.CSS_SELECTOR, "#root > div > div.MuiContainer-root.MuiContainer-maxWidthXl.css-15f64v6-MuiContainer-root > div > div.MuiPaper-root.MuiPaper-elevation.MuiPaper-rounded.MuiPaper-elevation1.css-b27thi-MuiPaper-root > div > div.MuiGrid2-root.MuiGrid2-container.MuiGrid2-direction-xs-row.MuiGrid2-spacing-xs-2.css-s356kp-MuiGrid2-root > div:nth-child(1)")
button1img = button1.find_element(By.CSS_SELECTOR, "img")
button1.click()

cat_element = driver.find_element(By.CSS_SELECTOR, 'div.MuiBox-root img[alt="cat"]')
cat_element.click()

button2 = driver.find_element(By.CSS_SELECTOR, "#root > div > div.MuiContainer-root.MuiContainer-maxWidthXl.css-15f64v6-MuiContainer-root > div > div.MuiPaper-root.MuiPaper-elevation.MuiPaper-rounded.MuiPaper-elevation1.css-b27thi-MuiPaper-root > div > div.MuiGrid2-root.MuiGrid2-container.MuiGrid2-direction-xs-row.MuiGrid2-spacing-xs-2.css-s356kp-MuiGrid2-root > div:nth-child(3)")
button2img = button2.find_element(By.CSS_SELECTOR, "img")
button2.click()

button3 = driver.find_element(By.CSS_SELECTOR, "#root > div > div.MuiContainer-root.MuiContainer-maxWidthXl.css-15f64v6-MuiContainer-root > div > div.MuiPaper-root.MuiPaper-elevation.MuiPaper-rounded.MuiPaper-elevation1.css-b27thi-MuiPaper-root > div > div.MuiGrid2-root.MuiGrid2-container.MuiGrid2-direction-xs-row.MuiGrid2-spacing-xs-2.css-s356kp-MuiGrid2-root > div:nth-child(5)")
button3img = button3.find_element(By.CSS_SELECTOR, "img")

for element in driver.find_elements(By.CLASS_NAME, "css-xdcm7u > div"):
  if element.get_attribute("class") == "css-1yyqevv-MuiImageListItem-root":
    continue
  actions.move_to_element(element)
  actions.perform()
  element.click()

  time.sleep(0.1)
  src = button3img.get_attribute('src')
  filename = src.split('/')[-1].split('?')[0]
  with open(f'{SAVE_DIRECTORY}/{filename}', 'wb') as file:
    response = requests.get(src)
    file.write(response.content)
  with open(f'{SAVE_DIRECTORY}/filename_to_alt.txt', 'a') as file:
    file.write(f'{filename}\t{button1img.get_attribute("alt")}\t{button2img.get_attribute("alt")}\t{button3img.get_attribute("alt")}\n')


print('finished. Press any key to exit.')
input()

driver.quit()
set_filename.py

import os

SRC_DIRECTORY=r'./emoji_kitchen'
SAVE_DIRECTORY=r'./emoji_kitchen_output'

os.makedirs(SAVE_DIRECTORY, exist_ok=True)

new_file_name = {}
with open(f'{SRC_DIRECTORY}/filename_to_alt.txt', 'r') as f:
  for line in f:
    parts = line.strip().split('\t')
    new_file_name[parts[0]] = f'{parts[1]}_{parts[2]}.png'

for root, dirs, files in os.walk(SRC_DIRECTORY):
  for file in files:
    if not file.endswith(".png"):
      continue
    src_path = os.path.join(root, file)
    dst_path = os.path.join(SAVE_DIRECTORY, new_file_name[file])
    with open(src_path, 'rb') as src_file:
      with open(dst_path, 'wb') as dst_file:
        dst_file.write(src_file.read())

実施手順

画像の取り込み

Emoji Kitchenのページに、GitHubへのリンクが張られている。
emoji-kitchen

これをgit cloneかDownload ZIPでダウンロードし、解凍先へchange directoryののち以下コマンドを実行

上記GitHubより転載
curl -L --compressed https://raw.githubusercontent.com/xsalazar/emoji-kitchen-backend/main/app/metadata.json -o src/Components/metadata.json
npm install && npm start

このコマンドにより、localhost:5173でEmoji KitchenのページのWebサーバが起動する。
これをSeleniumでスクレイピングする。

emoji_kitchen.pyのあるフォルダへchange directoryののち、以下コマンドを実行する。

スクレイピングコマンド
pip install selenium
pip install webdriver_manager
python ./emoji_kitchen.py

Seleum制御下のChromeが起動し、ぽちぽち押しながら./emoji_kitchenディレクトリへ画像が保存される。

取り込んだ絵文字の名前

併せて./emoji_kitchen/filename_to_alt.txtに、ファイル名と、imgタグのalt属性との対応表がタブ区切りで出力される。

file_name_to_alt.txt
u1fa84_u1f431.png	cat	magic_wand	magic_wand-cat
u1f600_u1f431.png	cat	grinning	grinning-cat
u1f603_u1f431.png	cat	smiley	smiley-cat
u1f604_u1f431.png	cat	smile	smile-cat
u1f601_u1f431.png	cat	grin	grin-cat
:
:

1列目はファイル名、2列目は組み合わせの1つ目の絵文字の名称、3列目は組み合わせの2つ目の絵文字の名称。4列目は組み合わせ絵文字の名称である。

絵文字のファイル名

ファイル名はアンダースコア区切りで、

  • 1つ目は組み合わせ1つ目の絵文字の文字コード
  • 2つ目は組み合わせ2つ目の絵文字の文字コード

である。
Unicodeであることを示すために、それぞれの文字コードには「u」が付加されている。実際の文字コードはuを外した残りの桁なので、それでGoogle検索などをすると該当する絵文字について説明したWebページが出てきたりする。

一部、以下のように、文字コードがハイフン区切りで長くなっているものがある。

u1f62e-u200d-u1f4a8_u1f431.png

このうちu200dの部分は「ゼロ幅接合子」と呼ばれるものであり、絵文字を組み合わせて合体するための制御文字(?)である。

なのでこの絵文字は「1f62e 200d 1f4a8」で1つの絵文字である点に注意する。

Slackにアップロードする準備

u1fa84_u1f431.pngなどの形式でSlackのカスタム絵文字へアップロードすると、以下のようにファイル名が絵文字の名前として自動入力され、実際に絵文字として使う際に名前で検索できなくて不便である。

image.png

ローカルで実行したEmoji Kitchenのページにおいて絵文字の名前がAlt属性に埋め込まれていることに着目し、これをファイル名として利用する。

以下コマンドを実行し、filename_to_alt.txtの対応表に従ってファイル名を変更する。

ファイル名を変えて別フォルダへ出力するコマンド
python ./set_filename.py

すると、emoji_kitchen_outputフォルダ以下にファイル名が変更された状態で出力されているはず。
これをSlackカスタム絵文字追加ページへアップロードすると、そのファイル名で絵文字の名前が自動入力される。

image.png

その他

cat_magic_wand_猫_魔法の杖のような名前にしたい場合は、
ファイル名から組み合わせもう一方の文字コードを抽出し、何らかの手段でこれに応じたja emoji nameを引き当ててファイル名に組み込むことができる。

Slackにアップロード

ワークスペースにカスタム絵文字とエイリアスを追加する

失敗1:カスタム絵文字一括登録

以下ソースコードで登録しようとしたけど、エラーが出てしまい
技能上の都合でどうにもならなかったので、
カスタム絵文字への登録は全部手作業で実施した。

add_many_emoji.py
import time

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.action_chains import ActionChains
from webdriver_manager.chrome import ChromeDriverManager
import requests
import os
import pickle

service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

action = webdriver.common.action_chains.ActionChains(driver)

SRC_DIRECTORY=r'./emoji_kitchen_output'

def login(driver):
  # ログインしてから、コンソールでEnterキーを押す
  input()

  cookies = driver.get_cookies() # クッキーを取得する
  pickle.dump(cookies,open('./add_many_emoji_cookie.pickle','wb'))
  exit(0)

driver.get(f"https://*******.slack.com/customize/emoji?utm_source=in-prod&utm_medium=inprod-customize_link-slack_menu-click")

if os.path.exists('./add_many_emoji_cookie.pickle'):
  cookies = pickle.load(open('./add_many_emoji_cookie.pickle', 'rb'))
  for cookie in cookies:
    driver.add_cookie(cookie)
  driver.refresh()
else:
  login(driver)

driver.implicitly_wait(10)

def clickforcible(action, element):
  action = webdriver.common.action_chains.ActionChains(driver)
  action.move_to_element_with_offset(element, 5, 5)
  action.click()
  action.perform()

def upload_emoji(driver: webdriver.Chrome, filename):
  button1 = driver.find_element(By.CLASS_NAME, 'p-customize_emoji_wrapper__custom_button_short')
  button1.click()
  driver.implicitly_wait(1)

  # button1 = driver.find_element(By.CSS_SELECTOR, '[data-qa="customize_emoji_add_dialog_upload"]')
  # button1.click()
  # driver.implicitly_wait(1)

  upload_input = driver.find_element(By.CSS_SELECTOR, '[data-qa="customize_emoji_add_dialog_file_input"]')
  upload_input.send_keys(os.path.join(SRC_DIRECTORY, filename))
  driver.implicitly_wait(1)

  print('ok')
  input()
  quit()

upload_emoji(driver, 'cat_two_hearts.png')

結果

かわいい

image.png

不満

猫絵文字が多すぎて、絵文字を検索するときに:catですべての猫絵文字が出なくなってしまった。(特に元から手書きで書いたやつ)
こんなにかわいいのだから、Slackはデフォルトの絵文字にEmoji Kitchenを追加するべき。

特筆すべき点

Emoji Kitchenでは100,000以上の組み合わせ絵文字をGoogleのデザインチームが愛情込めて手作業で作ったらしい。
マジ?

おまけ

Emoji Kitchenに出会うまでに筆者が手作業で作成した、ねこの絵文字をどうぞ;

black_cat_face
black_cat_face.png
clap_cat
clap-cat.png
angle_cat
angel.png
melting_cat
melting_cat.png
nyan_bowing
nyan_bowing.png
nyan_gesturing_ok
nyan_gesturing_ok.png
pray_cat
pray-cat.png
cat_hand
cat_hand.png
black_cat_hand
black_cat_hand.png

もっとつくるわよ~~(1個作るのに何時間もかかるので期待しないでお待ちください)

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?