Twitterでmarkdownをツイートするガチなchrome拡張作った

## 追記情報
- 2018.12.10: [機能追加](https://qiita.com/RyosukeCla/items/a4e165a3d171b6b4b5c0#%E8%BF%BD%E8%A8%98-1210---published-chrome-extension)
- 2018.12.14: [Chrome Extension公開](https://qiita.com/RyosukeCla/items/a4e165a3d171b6b4b5c0#%E8%BF%BD%E8%A8%98-1214)
    - [Chrome Web Store](https://chrome.google.com/webstore/detail/twitter-md/ofcdoemohfnfofjafamjbhdfjiifbehi)

## お前の二番煎じじゃね？
はい、そうですけど？

そうですけど、ガチなChrome拡張作ったから、とりあえず見て！！！！！

![tmd-3.gif](https://qiita-image-store.s3.amazonaws.com/0/183627/a2ef5759-d8cd-804c-ac80-a3106fc7e26a.gif)

<blockquote class="twitter-tweet" data-lang="ja"><p lang="und" dir="ltr"><a href="https://twitter.com/hashtag/TwitterMD?src=hash&amp;ref_src=twsrc%5Etfw">#TwitterMD</a> <a href="https://t.co/WfR1O0Moor">pic.twitter.com/WfR1O0Moor</a></p>&mdash; Ryosuke Suzuki (@GentleClarinet) <a href="https://twitter.com/GentleClarinet/status/1071521754240176128?ref_src=twsrc%5Etfw">2018年12月8日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ということで、[明治大学advent calendar](https://qiita.com/advent-calendar/2018/meiji_univ)やっていきます。

## Abstract
Twitterという素晴らしいSNSにマークダウンを画像として投稿できるようにする、ガチなChrome拡張を作った。

## Introduction
**皆さん、Twitterは使ってる？？？？**

僕は身体の一部って思っているレベルで使ってる。いや、もはや息を吸うレベルで使ってる。信じるか信じないはあなた次第。

Twitterは最高なんですけど、以下のような仕方がない問題があるよね。（多分、エンジニアとか理系な人たちが特に思っているんだろうな。）

- 数式が書けない
- コードが書けない
- 280文字ぐらい制限

上記のような問題に対して、Twitterユーザーは以下のようなことをするよね。

- 数式はtex記法か画像
- コードはplainか画像

これ、正直どうにかしたい。

## Related works
どうやら、解決策が幾つかあるそう。

### 1. [TwitterでMarkdownを使えるようにした](https://qiita.com/dala00/items/1f15c68cd771ea9c401a)
Chrome拡張で、Twitterでmarkdownをレンダリングするやりかた。
**Pros**

- markdownが使える
- sytax highlightされる

**Cons**

- Chrome拡張をインストールしているデバイスでのみmarkdownがレンダリングされる
- Twitterのeditorでmarkdownを書く必要がある
- 数式が使えない

### 2. [Mathtodon](https://mathtod.online/about)
あの、一時期、流行ってた分散型Twitterであるマストドン。マストドンに数式をレンダリングする機能を追加したインスタンス。(使ったことないからよくわからん。)

**Pros**

- 数式が書ける

**Cons**

- Twitterじゃない

### 3. [ritwee](https://ritwee.top/)
WYSWYGエディターでrichなツイートできるサービス。画像化してツイートするやり方。

**Pros**

- 画像としてツイートされるので、デバイス依存無し
- syntax highlight
- WYSWYG

**Cons**

- Ritweeのサイトに行かないとダメで、めんどい
- 数式が使えない
- WYSWYGが好きじゃない（個人的な好みだけど、皆、markdown好きでしょ？）

## My method
色々な解決策があるとはいえ、consの部分が痛いので、only prosな解決策を考えた。

### Overview
Chrome拡張でmarkdownを画像にし、Twitter APIで画像をツイートする。

<img width="1484" alt="スクリーンショット 2018-12-09 6.38.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/183627/bfac9038-8f22-11ff-aefb-47b0af421e6f.png">

### Chrome拡張
どのウェブサイトを見ていても、クローム拡張のポップアップならアクセシビリティが良い。Twitterを拡張するにはもってこい。

### md to html
`markdown-it`なり、`marked`なりのmd parserを使い、mdをhtmlにする。今回は`markdown-it`にした。

```js
import markdownIt from "markdown-it";

const mdParser = markdownIt({
  html: true,
  xhtmlOut: true,
  breaks: true,
  langPrefix: "language-",
  linkify: true,
});
```

### sytax highlight
syntax highlightする。今回は`highlihghtjs`を使う。

```js
import hljs from "highlightjs";
const mdParser = markdownIt({
  //...
  highlight: function(str, lang) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return hljs.highlight(lang, str).value;
      } catch (__) {}
    }

    return ""; // use external default escaping
  }
})
```

### Checkbox
markdown-itのcheckbox記法をパースしてくれるをpluginがある。

```js
import markdownCheckbox from "markdown-it-checkbox";
mdParser.use(markdownCheckbox);
```

### Math formula
有名どころだと`Mathjax`があるが、重いので使いたくない。今回は高速で有名な`katex`を使う。面倒なので、markdown-itにkatex pluginを使う。

```js
import markdownKatex from "markdown-it-katex";
mdParser.use(markdownKatex)
```

### html to image
画像に変換することが大事。これでデバイス依存無しにマークダウンをレンダリングできるようになる。

今回は、`html2canvas`というOSSを使い、domをcanvasにし、canvasからpngにする。ここで注意が必要なのは、html to canvasではなく、dom to canvasであること。しかも、domもinnerHtmlとかはダメだったりして、工夫が必要。

まずは、htmlからdomを作る。

```js
const parser = new DOMParser()
const wrapper = parser.parseFromString(html, "text/html");
```

wrapper.body以下のdomをブラウザでレンダリングさせる。

```js
const nodes = wrapper.body.childNodes;
const markdownBody = document.createElement("div");
nodes.forEach(node => {
    markdownBody.append(node);
  });
document.body.appendChild(markdownBody);
```

すると、canvasに変換することができるようになる。

```js
html2canvas(markdownBody)
  .then(canvas => {
    //...
  });
```

そして、画像にする。

```js
html2canvas(markdownBody)
  .then(canvas => {
    canvas.toBlob(blob => {
      //...
    });
  });
```

ここで、注意なのが、canvasで別オリジンの画像をレンダリングできないこと。なので、`Access-Control-Allow-Origin`ヘッダーを付与するためのproxy層が必要になる。

どうやら、`html2canvas`がCORS用のexpress middlewareを提供してくれているのでそれを使う。

```server.js
const express = require("express");
const app = express();
const proxy = require("html2canvas-proxy");
app.use("/corsProxy", proxy());
```

そして、`html2canvas`のオプションを設定する。

```js
html2canvas(markdownBody, {
    proxy: `${config.apiUrl}/corsProxy`,
    useCORS: true
  })
```

これで、mdを完璧にレンダリングできるようになる。

![download (2).png](https://qiita-image-store.s3.amazonaws.com/0/183627/16ddd3e0-ece4-b9bd-bae1-6925229ae123.png)


### Twitter api
nodejsの`twitter`モジュールを使う。まずは画像をアップロードしてから、ツイートする。

```server.js
const twitterClient = new Twitter({
    consumer_key: config.consumerKey,
    consumer_secret: config.consumerSecret,
    access_token_key: user.access_token,
    access_token_secret: user.access_token_secret
  });

  twitterClient.post("media/upload", { media: image }, (err, media) => {
    if (err) return

    const status = {
      status: "something",
      media_ids: media.media_id_string // Pass the media id string
    };

    twitterClient.post("statuses/update", status, function(err, tweet) {
      if (!err) console.log('done')
    });
  });
```

あとは、reactなりvueなりを使ってホゲホゲして最高の拡張を作って、終わり。

## [My work](https://github.com/RyosukeCla/twitter-md-chrome)
Chrome拡張を作った。名前は TwitterMD。[Github](https://github.com/RyosukeCla/twitter-md-chrome)

<img width="928" alt="スクリーンショット 2018-12-09 7.34.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/183627/715da85d-4c40-f7fd-2ee1-7c1f68bffadf.png">

**現在、chrome web storeに申請中。リジェクトされたった👿**

> We may remove your item if it has an unclear description field, or missing icons or screenshots, and appears to be suspicious

description書くの忘れてた。。。。

とりあえず、Chrome拡張はGithubに置いておきます。申請通ったら追記します。

数日で開発してソースコードが汚いのと、API KEYを直書きしてしまっているので、ソースコードは載せてません。いつか、綺麗にして公開します。create-react-app使わなければよかった。。。

## 追記: 12/10 - Published Chrome Extension
- chrome web storeにrejectされまくっていて辛い👿
- 機能追加
    - markdownのcssをカスタマイズ可能に
    - ツイート時の本文を設定可能に
    - Front Matter

<img width="623" alt="スクリーンショット 2018-12-10 21.01.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/183627/9460ad4d-fdec-96dd-954a-15f1a0f39690.png">
<img width="621" alt="スクリーンショット 2018-12-10 21.49.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/183627/e01b531f-23d9-cbe7-b638-3fee16e81f7a.png">

## 追記: 12/14
- chrome web extension ようやく公開できました。

[chrome web store](https://chrome.google.com/webstore/detail/twitter-md/ofcdoemohfnfofjafamjbhdfjiifbehi)

## おわりに

**僕/私も機能追加したい！なんて方が居たら連絡ください！**

連絡は[ここ(twitter)](https://twitter.com/GentleClarinet)までお願いします。