Chrome拡張を作ってweb版チャットワークを改造してみた

Chrome拡張を作ってweb版チャットワークを改造してみた
LINEで送る
Pocket

こんにちは。
とても今更ながら、Chrome拡張機能がHTMLとCSSとjsで作れるらしいので、作ってみました。

よくあるサンプルの、「ただalert出すだけ」だと芸がないので、
web版のチャットワークを改造しつつ、拡張機能について学んだメモです。

注意

この記事を執筆したのは2014/07/20です。

チャットワークのHTML構造に激しく依存するため、
HTMLやjsの回収があった場合には動作しなくなる可能性が非常に高いです。
情報が古くなり動かなくなる可能性があることを、あらかじめご了承ください。

制作するもの

コナミコマンドを実装するjsを利用して、

チャットワークのページ内で、
コナミコマンド(↑↑↓↓←→←→ba)が入力されたら、マイチャットにグラディウスのAAを投稿する

というしょうもない小ネタをやってみたいと思います。
ちなみに完成した物はこちらになります。

前提

ライブラリの導入にbowerを使用します。
インストールされている前提で話を進めます。ご了承下さい。

まずはalert

とりあえず動作確認のために、
チャットワークのドメイン(https://www.chatwork.com)ならalertを出すようにしてみます。

コナミコマンドを実現するjs、jQueryはbowerで配布されています。
あとは拡張機能に必要なmanifest.jsonと、自作するjsファイルを生成します。

manifest.jsonは以下の内容にします。

そして、main.jsにアラートだけ書きます。

この状態で、Chrome拡張機能としてChromeにインポートして動作確認します。

拡張機能をChromeにインポート

ストアで販売してない拡張機能でもインポートすることができます。

メニューから拡張機能のページを開き、

Step01

「パッケージ化されていない拡張機能を読み込む…」ボタンをクリックします。
選択するファイルは、先ほど作成したchrome-ext-gradiusフォルダです。

フォルダを選択すると、下記の画面のように拡張機能の一覧に現れます。

Step02

なお、一度取り込みを行ってしまえば、
ファイルを変更した時に拡張機能のページをリロードするだけで再読込されます。

リロードしないと拡張機能の再読み込みがされないので、
あれ動かない?と思ったら拡張機能のページをリロードしてみてください。

次に、Chrome拡張機能のcontent scriptsについての理解を深めます。

content scripts

content scriptsは任意のwebページに対して任意のjsを実行することができますが、いくつか制限があります。

コンテント・スクリプトは “Isolated World”(隔離空間) と呼ばれる特殊な環境で実行される。ここでは読み込まれたページのDOMにはアクセスできるが、該当ページのJavaScriptの変数や関数には出来ない。そのため、各コンテンツ・スクリプトからは、あたかも他のJavaScriptが全く実行されていないページで自分が実行されるように見える。逆もまた真なりで、各ページのスクリプトからもコンテンツ・スクリプトで定義した変数や関数にアクセスすることは出来ない。
コンテント・スクリプト | Chrome Extensions API リファレンス

このように、そのページで読み込まれているjsには一切アクセスできず、
拡張機能で定義した機能についてもそのページのjsからはアクセスできません。

ただし、DOMを介してデータのやりとりを行うことができます。

「DOMを書き換えることができて、どんな命名をしても対象ページ内のjsと衝突することはない」

と捉えるとかなり幸せに思いますが、
特定のページ用(今回ならチャットワーク専用)の拡張機能を作る場合には、
チャットワークで動作しているjsにアクセスすることができないため、いちいち各操作を自前で実装する必要があります。
例えば、チャットルームの切り替えは、タイミング制御まで合わせるとなかなかの鬼門です。

不幸中の幸いながら、
チャットワークのHTMLには属性として各種パラメータが埋め込まれているので、パース処理や値の取得自体は簡単です。

では早速実装していきます。

コナミコマンドを有効化

基本的にはcheet関数を使用してショートカットを設定するだけなのですが、

キーボードイベントに関して要注意なのは、チャットワークのjsと衝突して、
↑キーと↓キーが奪われてしまい、しかも握りつぶされています。

cheet.jsはwindowにイベントを設定しているので、
documentで止められてしまうと反応できません。

つらい。これはつらい。

と思って心が折れかけましたが、無理やりイベントを先取りすることが出来ました。

カスタムイベントを作りwindowへぶん投げるようにしました。

拡張機能をリロードし、チャットワークもリロードし、
「↑ ↑ ↓ ↓ ← → ← → B A」と入力してみると、以下のようになります。

Step03

cheet.js側が反応するようになったのでOKです。次へ進みます。

ユーティリティを作成

などの部分を直接書くと煩雑になるため、
DOM操作や記法のユーティリティは、作成しておいたchatwork-util.jsのほうに記述していきます。

[info]タグ記法のテキストを生成するユーティリティ

チャットワークでは、[info]というタグが使えます。
AAなど等幅にしてほしいテキストを送る場合に便利です。

使用するのでユーティリティ化します。

マイチャットのHTML要素を取得するユーティリティ

チャットワークの左側にあるチャットルーム一覧は、
頻繁にDOMの書き換えが行われるため、変数にキャッシュすると期待通りの動作になりません。
なのでメソッド化して、毎度HTMLに問い合わせて取得します。

なお、全体を記述すると長くなってしまうので、
ファイル全体ではなく必要な箇所のみ記述します。

マイチャットを取得出来ました。
何らかのフラグではなく、チャットルームの名前とMYCHAT_NAMEという変数の値に依存してしまっているので、治せる方法があれば治したいです。

マイチャットへ切り替えるユーティリティ

マイチャットの要素を取得できたので、マイチャットへ切り替えるユーティリティも作成しておきます。

実装は簡単で、単にclickイベントを発行するだけです。

これだけです。

…なわけありません。
チャットルームの切替時に非同期でAjaxが走り、
切り替え先のチャットのメッセージ一覧を取得しメッセージ一覧画面をかきえます。
このチャットルームの切り替え完了を検知するタイミングの制御が、なかなか難しいです。

制御に失敗すると、チャットへ投稿するときに、別のチャットへ投稿してしまうことが起こります。
ですが、詳細までお話していると今回本筋から大きくそれるため触れません。
MutationObserverというオブジェクトを利用しています。詳しくはリポジトリを御覧ください。

チャットに投稿するユーティリティ

前述のタイミング制御の問題さえなければごくシンプルです。

チャット内容入力フォームと送信ボタンのHTMLは、
書き換えられることがないので、速度のため変数にキャッシュしています。

動作確認をしてみます。

Screen Shot 2014 07 20 at 5 30 13 PM

OKです。

さて、マイチャットに切り替えられるようになり、発言もできるようになったので、
早速AAを投稿してみたいと思います。

AAを投稿する

今回使用するAAはこちら。

はい。ビックバイパーです。グラディウスやったことないけど。

ナメてんのかって感じの良いAAですね。
こちらのサイト様に掲載されていたAAを少し改変させていただきました。

あまり複雑なAAだと分かりにくいと思ったので、シンプルなAAで行きます。

ユーティリティは揃えらので、
あとはもうコナミコマンドのコールバックを仕上げるだけです。

main.jsの内容は以下の通りになります。

コナミコマンドを入力してみます。

Screen Shot 2014 07 20 at 5 46 36 PM

OKです。

どのチャットに居てもマイチャットへ移動し、AAが投稿されます。
動作がとても重い環境だと投稿しそこねたり、マイチャット以外へ投稿してしまうこともあるのでご注意ください。

ということで拡張機能が完成しました。
拡張機能ではCSSを読み込んだりすることもできるようなので、
もっと深めてみたら面白いことになりそうです。

完成したファイルはこちらからご覧になることができます。

Leko/chrome-ext-gradius · GitHub

参考

Google Chrome拡張機能入門 (全20回) – プログラミングならドットインストール

コンテント・スクリプト | Chrome Extensions API リファレンス

グラディウスAA集 (ボスのアスキーアート)

LINEで送る
Pocket