Slackのカスタム絵文字を一括ダウンロード・削除するChrome拡張を作ってみた

# できたもの

**2020/02/03 追記**
機能を追加して公開しました
[chromeウェブストア - Slack Custom Emoji Manager](https://chrome.google.com/webstore/detail/slack-custom-emoji-manage/cgipifjpcbhdppbjjphmgkmmgbeaggpc)


Slackカスタム絵文字の管理画面に、「一括ダウンロードするボタン」と「一括削除するボタン」を追加しました。
一括アップロードはすでにあるので([Neutral Face Emoji Tools](https://chrome.google.com/webstore/detail/neutral-face-emoji-tools/anchoacphlfbdomdlomnbbfhcmcdmjej))作ってないです。

読み込み前
<img width="987" alt="スクリーンショット 2019-12-21 23.55.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258777/e8b4aab7-6350-2c39-5375-dbc23dca49a0.png">
読み込み後
<img width="983" alt="スクリーンショット 2019-12-21 23.56.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258777/de4f5fd1-05b2-9d2c-d8cf-e61152ad9009.png">

# 作っていく

ほぼ自分用のメモですが、参考になれば

## 諸準備

### npmの初期化

```console
$ npm init # 適当に質問に答える
```
### webpackの設定

```console
$ npm install -D webpack webpack-cli@beta # 一旦webpack-cliをbetaでinstall
```
```console
$ npx webpack-cli create
? Will your application have multiple bundles? No
? Which will be your application entry point? src/index
? In which folder do you want to store your generated bundles? 'dist'
? Will you use one of the below JS solutions? Typescript
? Will you use one of the below CSS solutions? CSS
? If you want to bundle your CSS files, what will you name the bundle? (press enter to skip) main
? Overwrite package.json? overwrite
```

```console
npm install webpack-cli@3 # installし直す
```

### ESLint

```console
$ npm install -D eslint
```
```console
$ npx eslint --init
? How would you like to use ESLint? To check syntax, find problems, and enforce code style
? What type of modules does your project use? JavaScript modules (import/export)
? Which framework does your project use? None of these
? Does your project use TypeScript? Yes
? Where does your code run? Browser
? How would you like to define a style for your project? Use a popular style guide
? Which style guide do you want to follow? Airbnb: https://github.com/airbnb/javascript
? What format do you want your config file to be in? JavaScript
? Would you like to install them now with npm? Yes
```

### Prettier

```console
$ npm install -D prettier eslint-plugin-prettier eslint-config-prettier
```

```js
module.exports = {
  env: {
    browser: true,
    es6: true
  },
  extends: ["airbnb-base", "plugin:prettier/recommended"], // "plugin:prettier/recommended"を追加
  globals: {
    Atomics: "readonly",
    SharedArrayBuffer: "readonly"
  },
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: "module"
  },
  plugins: ["@typescript-eslint", "prettier"], // "prettier"を追加
  rules: {
    "prettier/prettier": "error" // 追加
  }
};
```

#### .gitignore
```
$ gibo Node VisualStudioCode >> .gitignore
```

## 作成

### ビルド

`package.json`を以下のように変更

```package.json
...
"scripts": {
  ...
  "build": "rm -rf dist && webpack"
}
...
```

下記コマンドでビルドできます

```console
$ npm run build
```

`dist`ディレクトリが作成されていれば成功



### manifest.jsonを作成

目的に合わせて異なるので参考までに。
参考: [Chrome 拡張機能のマニフェストファイルの書き方](https://qiita.com/mdstoy/items/9866544e37987337dc79)

```manifest.json
{
  "manifest_version": 2,
  "name": "Slack Custom Emoji Manager",
  "short_name": "Emoji Manager",
  "description": "Management to slack custom emoji.",
  "version": "1.0",
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  },
  "browser_action": {
    "default_icon": {
      "19": "icon24.png"
    }
  },
  "content_scripts": [
    {
      "js": ["content.js"],
      "matches": ["*://*.slack.com/*/emoji*"]
    }
  ],
  "background": {
    "scripts": ["background.js"]
  },
  "permissions": [
    "webRequest",
    "webRequestBlocking",
    "*://emoji.slack-edge.com/",
    "*://slack.com/api/"
  ],
  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
}
```

### Chrome用に設定を変更

#### Icon, manifest.jsonを`dist`にコピー

`copy-webpack-plugin`を使用してIcon画像等を`dist`に出力するようにします

```console
npm install -D copy-webpack-plugin
```

また、今回は`content.js`と`background.js`を吐き出したいので、そのための設定も追加します

```webpack.config.js
...
const CopyPlugin = require('copy-webpack-plugin');

module.exports = {
  ...
  entry: {
    content: path.resolve(__dirname, './src/content.ts'),
    background: path.resolve(__dirname, './src/background.ts')
  },
  ...
  plugins: [
    ...
    new CopyPlugin([{ from: './public', to: './' }])
    // fromにコピーしたいファイルがあるpathを指定
  ],
  ...
}
```

### Chromeに読み込ませる

- chromeで`chrome://extensions/`を開きます
- デベロッパーモードにする
- `パッケージ化されていない拡張機能を読み込む`をクリック
- `dist`ディレクトリ選択
- ビルドし直した際は、再度読み込みが必要


### 動かすスクリプトを書く

良い感じに書く。

[Neutral Face Emoji Tools](https://chrome.google.com/webstore/detail/neutral-face-emoji-tools/anchoacphlfbdomdlomnbbfhcmcdmjej)の[ソースコード](https://github.com/Fauntleroy/neutral-face-emoji-tools)を参考にして書きました。

ソースコードはこちら
https://github.com/nabekou29/slack_custom_emoji_manager

## 詰まった箇所

### 画像が取得できない

ダウンロードの際に画像を取得しようと思ったが少し詰まったのでメモ

```console
Refused to set unsafe header "User-Agent"
```

URLから画像をaxiosで取得する際にこのエラーが出て取得できなかった。
解決方法としては`"User-Agent"`をそもそもセットしなければ大丈夫らしい。

`background.ts`を追加して設定を変更します

```ts:background.ts
chrome.webRequest.onBeforeSendHeaders.addListener(
  details => {
    const requestHeaders = details.requestHeaders?.filter(
      h => h.name !== 'User-Agent'
    );
    return { requestHeaders };
  },
  { urls: ['<all_urls>'] },
  ['blocking', 'requestHeaders']
);
```

```manifest.json
{
  ...
  "background": {
    "scripts": ["background.js"]
  },
  "permissions": [
    "webRequest",
    "webRequestBlocking",
    "*://emoji.slack-edge.com/"
  ],
  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
}
```

```js:webpack.config.js
module.exports = {
  mode: 'development',
  entry: {
    content: `${__dirname}/src/content.ts`,
    background: `${__dirname}/src/background.ts` // 追加
  },
  ...
}

```

参考: https://developer.chrome.com/extensions/webRequest

# 参考
- https://github.com/Fauntleroy/neutral-face-emoji-tools
- [【初心者】chrome拡張をTypeScriptで開発するときのWebpackの設定](https://qiita.com/okumurakengo/items/1a4404c20b0bf10f2c68)
- [Chrome 拡張機能のマニフェストファイルの書き方](https://qiita.com/mdstoy/items/9866544e37987337dc79)
- [クライアントサイドでzipファイルを生成して、ダウンロードする](https://blog.imo-tikuwa.com/zip-compress-and-download-on-clientside/)