Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API経由でSlackへカスタム絵文字を追加できないか調査したときのメモ #16

Open
yamamoto-yuta opened this issue May 6, 2023 · 2 comments

Comments

@yamamoto-yuta
Copy link
Owner

yamamoto-yuta commented May 6, 2023

# 必要に合わせてコメントアウトを外して記載してください

# 記事の説明文(無い場合は本文先頭200文字を使用)
#ogp_description:

# サムネイル画像のテーマ -> 'default' or 'upload'
thumbnail_theme: default

# サムネイル画像の背景画像(1280x670px推奨, なくてもOK)
#thumbnail_image_url: 

# 予約投稿の日時(無い場合は現在時刻を使用)
#posted_at: YYYY-MM-DD hh:mm

やりたいこと

Slack API 経由で Slack へ絵文字を追加したい

調べたこと

方針1: Slack API の admin.emoji.add を利用する

公式で用意されている絵文字追加用 API。

公式ドキュメント: https://api.slack.com/methods/admin.emoji.add

ただし、結局この API は使えなかった。理由はこの API を利用するには Slack App に admin.teams:write という権限を付与する必要があったから。

admin.teams:write についての公式ドキュメント: https://api.slack.com/scopes/admin.teams:write

これには次の問題があったので今回は方針を変更することにした:

  • Admin API は個々のワークスペースではなく組織全体に影響を与えることができ、ただ絵文字を追加するだけの用途で付与するには不適当なため
  • そもそも Enterprise 版でないと利用できないため

方針2: 一般公開されていない API /api/emoji.add を利用する

どうやら Slack には一般公開されていない API がいくつかあるらしく、その中の一つである /api/emoji.add を利用すれば絵文字の追加が可能らしい:

参考: slackhq/slack-api-docs#28 (comment)

この API を利用しているプロダクトがいくつかあるようだったので、それらを参考に API を叩いてみた。 API を叩く手順は次の通り。

/api/emoji.add API を叩く手順

1. トークンを入手する
  1. 絵文字を追加したいワークスペースを開く
  2. 開発者ツールを開き、コンソールタブで次のコマンドを実行する
JSON.parse(localStorage.localConfig_v2).teams[document.location.pathname.match(/^\/client\/(T[A-Z0-9]+)/)[1]].token

引用: https://github.com/jackellenberger/emojme/blob/master/README.md#cookie-token-one-liner

xoxc-xxxxxxxxxx といった形式のトークンが返ってくるはず。

2. Cookie を入手する
  1. https://<TEAM_ID>.slack.com/customize/emoji を開く(ワークスペースを開いた状態で「以下をカスタマイズ」をクリックすると飛べる)
  2. 開発者ツールを開き、「ネットワーク」タブを開く
  3. emoji というドキュメントを選択する
  4. リクエストヘッダーの cookie: の値が入手したい Cookie (この値をコピーするとき、「右クリック > 『値をコピー』」だとコピー後の文字列に日本語が混じって後で困るので、範囲選択でコピーする必要がある)
3. API を叩く

API を叩くコード:

import requests

TOKEN = "xoxc-xxxxxxxxxx"   # トークン
COOKIE = "xxxxxxxxxx"   # Cookie

team_name = "xxxxxxxxxx"    # ワークスペースのチーム名
emoji_name = "emoji"   # 絵文字の名前
emoji_img_filepath = "./emoji.png"   # 絵文字の画像ファイルへのパス

URL_ADD = "https://{team_name}.slack.com/api/emoji.add"
r = requests.post(
    URL_ADD.format(team_name=team_name),
    headers = {'Cookie': COOKIE},
    data = {
        'mode': 'data',
        'name': emoji_name,
        'token': TOKEN
    },
    files={'image': open(emoji_img_filepath, 'rb')}
)

次のレスポンスが返ってきていれば成功:

{"ok":true}

残る課題

  • トークンと Cookie の入手を自動でできないか?

余談

/api/emoji.add の xoxb トークンでの利用について

/api/emoji.add を xoxb トークンで利用できるよう求める Issue が2019年1月に建てられているが、2023年5月現在、まだ Open なままである。

該当 Issue: slackhq/slack-api-docs#95

xoxc トークンをスクレイピングで入手する

https://<TEAM_ID>.slack.com/customize/emoji ページをスクレイピングすることで xoxc トークンを入手している例を紹介している 記事 を見つけた。

該当コード: https://github.com/smashwilson/slack-emojinator/blob/fbcf759ebbda8bd37b77c91362edde9fd3e0c05a/upload.py#L81-L94

しかし、やってみたがうまくいかなかった( api_token: の値が "" だった)。やってみた当時はまだトークンや Cookie の入手などが手探りの状態だったので、それが原因でうまくいかなかったのかも…?

参考

@petite-etoile
Copy link

@yamamoto-yuta
突然すみません。
API経由でSlackにカスタム絵文字を追加するときにこちらのissue参考にさせていただいたので、情報共有します。

残る課題

トークンと Cookie の入手を自動でできないか?

seleniumなどのブラウザ自動化ライブラリで https://<TEAM_ID>.slack.com/customize/emoji にアクセスしてapi-tokenやcookieを取得することができます。

↓ 現在は自分のリポジトリはpublicですが、今後privateになる可能性があるのでコード直書きします ↓

const { Builder, By, Key, until } = require("selenium-webdriver");
const chrome = require("selenium-webdriver/chrome");

// 環境変数の読み込み
require("dotenv").config();
const workspace = process.env.SLACK_WORKSPACE;
const email = process.env.EMAIL;
const password = process.env.PASSWORD;

/**
 * Seleniumを使ってサインインしてCookieとAPIトークンを取得する関数.
 * @returns {Promise<object>} - CookieとAPIトークン
 */
async function getCookieAndToken() {
  // Seleniumのドライバーをビルド
  const driver = await buildSeleniumDriver();

  try {
    // サインイン
    await signInToSlack(driver);

    cookie = await getCookie(driver);
    token = await getToken(driver);
  } finally {
    // ドライバーを終了
    await driver.quit();
  }
  return { cookie, token };
}

/**
 * Seleniumのドライバーをビルドする関数
 * @returns {WebDriver} - Seleniumのドライバー
 */
async function buildSeleniumDriver() {
  // ヘッドレスモードのオプションを設定
  const options = new chrome.Options();
  options.headless();

  // ヘッドレスモードでChromeを起動
  return await new Builder()
    .forBrowser("chrome")
    .setChromeOptions(options)
    .build();
}

/**
 * Slackにサインインする関数
 * @param {WebDriver} driver - Seleniumのドライバー
 * @returns {Promise<void>} - Promise
 */
async function signInToSlack(driver) {
  console.log("Signing in to Slack...");

  // Slackにアクセス
  await driver.get(
    `https://${workspace}.slack.com/sign_in_with_password?redir=%2Fcustomize%2Femoji#/`
  );

  // ログイン処理
  await driver.findElement(By.id("email")).sendKeys(email);
  await driver.findElement(By.id("password")).sendKeys(password, Key.RETURN);

  // 'boot_data'を含むscriptタグが読みこまれるのを待機 (api-tokenを取得するのに必要)
  await driver.wait(
    until.elementLocated(By.xpath("//script[contains(text(), 'boot_data')]")),
    10000
  );

  // カスタム絵文字セクションが読み込まれるの待機
  await driver.wait(until.elementLocated(By.id("list_emoji_section")), 10000);

  console.log("completed sign in to Slack");
}

/**
 * Cookieを取得する関数
 * @param {WebDriver} driver - Seleniumのドライバー
 * @returns {Promise<string>} - Cookie
 */
async function getCookie(driver) {
  const cookies = await driver.manage().getCookies();
  return cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join("; ");
}

/**
 * APIトークンを取得する関数
 * @param {WebDriver} driver - Seleniumのドライバー
 * @returns {Promise<string>} - APIトークン
 */
async function getToken(driver) {
  return driver.executeScript("return window.boot_data.api_token;");
}

module.exports = getCookieAndToken;

https://github.com/petite-etoile/LineStampSlackBot/pull/6

@yamamoto-yuta
Copy link
Owner Author

@petite-etoile
いえいえ、記事がお役に立って良かったです!
また、情報共有ありがとうございます!(ソースコードまで載せていただいて、とても助かります…! 🙏 )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants