主に SlackをTwitterのタイムラインライクに閲覧できるクライアント を作ったときにハマった話を書きます。
この記事はオプトテクノロジーズアドベントカレンダー21日目のエントリーです。
20日目は @yorishigeito さんの「開発合宿でIoTした」です
22日目は @hey_cube さんの「Storybook + zisui + reg-suit な visual testing を構築する」です
前提
アプリ要件
- Slackのメッセージをほぼリアルタイムに受け取って表示したい
- アプリ入れさせたくないのでwebサービスとして提供したい
以上の要件を満たすためにRTM APIを選択
Event APIという線もあったが、ngrok使ったりして開発するの地味に面倒そうだった(と思って試さなかったのがもしかしたら大失敗だったかも)
ハマり・つらみ
ハマり
-
@slack/client
の RTM API実装がnodeじゃないと動かない - OAuth Scopeの
client
とidentity.basic
を同時に許可する事ができない - passport.jsのslackプラグインで使い物にならないものが乱立している
つらみ
-
rtm.start
エンドポイントの API rate limitが少なすぎる -
@slack/client
のRTMClient#start
でコネクション確立中に再度コネクション確立要求をすると例外を吐く - slackのテキストパーサー・コンバータがない
- APIの仕様にドキュメントが追いついてない
- そもそもSlackを再実装するのが大変
解説していきます
@slack/client
の RTM API実装がnodeじゃないと動かない
まんまです。
これが理由で、なんらかのnode.jsが動くバックエンド実装が必要になってしまう
実際はelectronを使うかnodeでサーバーを立てるかの2択。
EventAPIを使っていたとしてもサーバーが必要になるので、どちらにせよフロントエンドで完結出来ないので手軽でない。。
OAuth Scopeの client
と identity.basic
を同時に許可する事ができない
アプリを実際に使うにあたって、ユーザーはOAuthの認可フローを通す必要がある(ユースケースによってはSlack Button を利用することで省略も可能)。
このときに、RTM APIを使うのに必要な client
という権限と、自分自身のユーザー情報を取得するのに必要な identity.basic
という権限を同時に認可しようとするとSlackに以下のようなエラーメッセージと共に突っ返される
Invalid permissions requested
Cannot request object scopes with deprecated scopes
ドキュメントからの推測しかできないが、おそらく Scopeのページで legacy
ラベルが張られている権限はその他のstableな権限と同時にリクエスト出来ないのではないかと予想される(実際試してみると client
, admin
のような組み合わせは通る)
この問題が解決できないと、ユーザーがアプリを動かすに至れないのでなんとかする必要があったが、どこにも情報がなかったのでかなり苦労した。
実際に作ったアプリでは、「 identity.basic
の権限を許可する」というフローのあとに、「 client
の権限を許可する」というフローを踏ませるワークアラウンドでなんとかした
これがほんとの二段階認証!
実際の動きはこんな感じ。最悪のUX。
https://gyazo.com/84cf702b999d7ac65c60f3f62e511210
passport.jsのslackプラグインで使い物にならないものが乱立している
気をつけましょう・・・
passport.jsでOAuth認可フローを実装する場合、大抵 passport-(サービス名)
という名前のライブラリを使うことになるが、 passport-slack
は普通にバグっていて動かない上にメンテもされていないという状態になっている。
(普通に認可するだけだといいが、二段階認証(違う)を実装する都合でバグを踏んだみたいなのもあり・・・)
最終的には passport-slack-oauth2
を使って動かせたのでよかったものの、結構辛かった・・・
rtm.start
エンドポイントの API rate limitが少なすぎる
rate limitがひどく低く、1分間に数回でlimitになる ( https://api.slack.com/methods/rtm.start )
またこれはあとから気づいたことだけど、SlackAPIはOAuthトークンごとのlimitではなく、ワークスペース単位のlimitなので、誰にでも使えるサービスとして提供するのに耐えられない(致命的)
https://api.slack.com/docs/rate-limits#overview
今回作ったアプリは、アプリのサーバーがユーザーごとにSlackとのコネクションを作り、リアルタイムにメッセージを受け取る形をとっている。
そのためnodeサーバーが再起動したときには、その時点で接続していたユーザーのコネクションを全て再接続したいところだが、それを安直にやるとrate limitに引っかかってしまう。つらい(つらい
ちなみにこの問題は時間がなかったので未解決・・・
@slack/client
の RTMClient#start
でコネクション確立中に再度コネクション確立要求をすると例外を吐く
例えば「とあるユーザーがこのアプリを使い始めて、認可フローを終えてアプリに戻ってきたタイミング(=初めてアプリのサーバーがSlackとコネクションを確立するタイミング)で、コネクションが成立する前にブラウザをリロードする」みたいな状況で死ぬ
しかもこの場合、ユーザー的にはあとからの接続要求が通ってほしいが、サーバーとしてはあとから来たほうで例外が出るのでフロントにコネクション成立した状態で返せないという状態になる(アプリの作りも悪いとは思う)
また、 RTMClient
はコネクション確立中というステータスを取れないので、回避もままならない。例外をcatchしてもポーリングするしかなくて、先述したrate limitも相まって非常に難解な問題だった(なお未解決。。。
slackのテキストパーサー・コンバータがない
そのまんま。
markdownライクなあの記法をちゃんと解釈してhtmlにしたいけど、それを実現するライブラリがなさそう(あったら教えてください)
たとえばSlackの取り消し線は ~文字列~
だが、これを 文字列 のように変更するという実装が簡単にできない。
一応ドキュメントは あるが、実装するのが大変・・・
APIの仕様にドキュメントが追いついてない
つらい。ドキュメント書くのは大変だからしょうがないという気持ちもありつつ。。。
たとえば message
イベントのドキュメントでは解説されていないメッセージがバンバン飛んでくるので色んなメッセージを叩いてがんばりましょうという感じ。
botやアプリ連携で投稿されるCircleCIなどの投稿が代表(だったと思う)
そもそもSlackを再実装するのが大変
当たり前。
クライアント作ってみて思ったが、Slack社はすごい。すごい!!
おわりに
つらかった(小並感)