やおよろず

ゆるふわな内容を書いても良い。と言うか何でも良い。そんな自由度の高いブログをめざします。

Slackに”いつものチャンネル”的な要素を持たせるChrome拡張を作った話

この記事は圧倒的令和ッ!!ぴょこりんクラスタ Advent Calendar 2019 - Adventarのために書いたものです。

はじめに

Slackってあるじゃないですか。
人によって馴染みの具合は様々かと思いますが、仕事にプライベートに技術コミュニティに、僕は何かと良く利用しています。
特に職場でのコミュニケーションがslackだと、驚く程色々なチャンネルを行き来したりします。*1

そんな中でたまに気になってくるのが、チャンネルの管理についてです。
お気に入り機能を使えば優先度の高/低2種類のカテゴライズまでは可能なのですが、それ以上の分類はできません。
僕はお気に入りチャンネルに自身の分報*2チャンネルと障害対応用のチャンネルを入れているのですが、
分報チャンネルと障害対応チャンネルがお気に入り枠内部で隣り合わせになっているのは、選択ミスのリスクが高くなります。

と言うか正直に言ってしまえば、先日まさに誤爆をしました。
障害対応チャンネルで思考過程を垂れ流しながら「なんで!?」って騒いでたの、今でも穴があったら入りたくなります。

分報チャンネルに情報流しながら試行錯誤している最中、ふと他チャンネルに移動して気が散っているのが良くないし、
自身の分報チャンネルに遷移する方法が一般のお気に入りチャンネルと変わらないと言うのもよくありません。
分報チャンネルは作業に集中している間に常時表示しているチャンネルなのですから、
お気に入りチャンネル以上の「いつもの」的な取り扱いが欲しいところです。

本題

と言うわけで今回は、以下の条件を満たすchrome拡張を作ってみました。

  • slackのワークスペースごとに一つ「いつものチャンネル」を登録できる
  • いつもとは違う箇所のワンクリックで、いつものチャンネルに遷移できる
  • ついでにそのチャンネル以外の存在を見えないものにする

ソースコードとデモ

ソースコードgithub.com で、デモは

https://github.com/yaoshimax/SlackRestrictor/raw/movie/demo.gif

です。

はい。これだけです。シンプルですね。

デモではチャンネル登録後にslackをreloadしていますが、冷静に考えたらその処理は不要でした。

構造の説明

  • options.htmlとoption.jsで設定。addボタンやdeleteボタンを押すとリストの追加/削除の上、storageに保存される
    • 入力内容のバリデーション? そんなものはない。
  • backrgound.jsでボタン押下時のリスナー登録がされてて、ボタンを押されたらどこかに向けてメッセージを飛ばす
  • slack系ページに仕込まれるcontent.jsに上述のメッセージを受け取て発火するリスナを仕込まれている。
    • リスナはメッセージを受けたらstorageのチャンネルを一通り嘗めつつsidebarに該当物があったらページ遷移&他チャンネル非表示化&ブラウザバック禁止

をしています。

苦労したこと、嵌ったこと、未解決問題

そもそもchrome拡張は●年前にお遊びで一つ作ろうとしたことがある程度で、細かなことは記憶の彼方でした。
当時作ろうとしたものもpopup windowがでれば良い程度の今回以上にシンプルなものだったし、実質初心者。
javascriptを触るのも去年のアドベントカレンダーぶりだし、初心者らしい失敗が今回は多かったです。

以下でそれらを軽く振り返って見ます。

はじめはslack API叩こうとしてたけど断念

初期構想では、右側にサイドバーが出てきて1チャンネル限定で表示するようなミニslack clientを作ろうとしてました。
ただし、これをやろうとするとslack API叩く際に渡すトークンが必要で、

Slack API: Applications | Slack あたりでアプリ登録して
アプリ画面からincoming-webhookの機能登録して必要な権限付与してトークン発行

みたいな手続きが必要となってしまいます。

今回の需要としては各ワークスペースごとに「このURLだけ使えるようにする!」みたいなことができれば良いので、
slack APIをなんとか使わない方向で…と路線変更して生まれたのが今回のchrome拡張です。
その結果、slackのhtml構造を理解して良い感じに改変するスキルが必要にはなりましたが、
API叩くよりはこのくらいでURL登録すればおっけー、くらいが自分に優しくて良いです。

sendMessageには2種類ある

slackの表示を制御するにはページ内に埋め込むスクリプトを(content.js)を何らかの方法で発火させる必要があって、
その方法としてメッセージと言うものが提供されている、と言う所までは良かったのですが、
メッセージを送る実装を書いた所でcontent.jsが全く受信しなくて少し詰まりました。

Message Passing - Google Chrome にちゃんと書いてあると言えば書いてあるのですが

Sending a request from a content script looks like this:
(中略)
Sending a request from the extension to a content script looks very similar, except that you need to specify which tab to send it to.

とあるように、content_script "から”メッセージを送る時のsendMessageはなんも考えずにsendすれば良いが、
content_script"に対して" メッセージを送る時は、どのタブの相手に送るか指定が必要。よってインターフェースも異なるそうな。
これに気付かずにruntime.sendMessageを書いてしまっていたのが今回の敗因。
ちゃんと原典読まずに付け焼き刃のググった内容を見て開発しようとするからこうなる…

発火しないclick

これはもう本当jqueryを少しでも触ってる人だったら当たり前だろって話なんですが、
久しぶりに触ったせいか完全にぼけていた話です。

$("#id-name")[0].click();

と書くべき所を

$("#id-name").click();

と書いててクリック発火しないのなんで???と暫く悩んでました。
id名指定だと要素は一意に定まるはずだから一要素のリストじゃなくてリストそのものが帰って来るような気がしちゃうやつ。

(未解決問題) ブラウザバックをどう防ぐべきか問題

調べていると、 qiita.com

的な解決策がすぐ出てくるのでサクッと書いてはみたのですが、
今回のケースだと別チャンネルからいつものチャンネルに遷移するような場合に、
ブラウザバックで別チャンネルに戻れてしまう問題がありました。
試行錯誤した感じ、click(); イベントの完了(もしくはそれに連動してurlが変わるまで)を待たずして上記処理が実行されてしまうことで、 pushStateをする内容が遷移前ページで固定されてしまうようなのです。

とりあえずは…と言う事で、今は https://github.com/yaoshimax/SlackRestrictor/blob/master/script/content.js#L28のように
sleepを入れて対応をしています。アンチパターン感が半端ない。

url変わるのを短い間隔でチェックしながら変わり次第反映…と言う手もありますが、
絶妙なタイミングで別ページに遷移された場合等など考えるとどういった処理が正解なのかは不明。教えて偉い人。

(未解決問題) 非表示状態のはずのチャンネルが何故か稀に復活してしまう

最後の最後にこれ言うのかって話ですが、致命的欠陥ですね。
月曜日に職場で運用していて発覚しました。そして再現条件が不明です。
ずっと使って居るといつの間にかひょっこり表示されるチャンネルが現れるのですが、
新規未読メッセージが来たチャンネル全てって訳ではないしメンションが来たとかそう言う特異なケースでもない。
どうやって消す?jQueryで要素を非表示にする方法4つ | BeGeek あたりを見て、
hide()/show()を使う方法からcss要素変更する形に変えもしましたが、改善せずです。

なんか特定条件で一瞬要素に!importantかなにかつけてるか、 クラス・roleの着いてない状態での追加→後付けでロールやらクラスやら付与、みたいな形で要素追加しているのか。
それにしたってあまり法則性が見いだせないのがやっかいですね。うーん、こまった。

終りに

とてもとても締まらないオチとなってしまいましたが、
久しぶりにjavascriptと戯れたり、普段とは違う類いの開発をやって見て楽しかったです。

ちなみに自作キーボードで開発してみた使用感として、まあ案の上ですが、記号系を出力するのが鬼門でした。
特に面倒なのが{と}で、片手じゃshiftと[]キーの感覚が広すぎるため、
右手小指で右シフト+左手人差し指で「キー、もしくはその逆、みたいなコマンドを強いられます。
さながらポップンの左右振り譜面…
あとは記号を色々押そうとするとShift押すべきかLower押すべきかRaise押すべきかがよくごちゃつくのも今後の課題ですね。
{[^=" とか、shift/同時押し無し/lower/raise/shift/ 見たいな運指になるのカオス。
これ効率良くなるのか…?と首を傾げてしまうこともありますが、まだ使いたてだし開発も今回が初めて。 徐々にカスタマイズと練習を積み重ねてよりよい方法を模索していきたいですね。

*1:調べて見たところ、職場slackで僕がjoinしているチャンネルの数は3桁いってました。勿論activeでないものも含みますが

*2:分報って何?と思った方はhttps://developer.ntt.com/ja/blog/d55e2be0-255d-4321-9d45-8608ce9d9726 あたりを見てください