知識なしでMastodonを改造する

先週の記事でも言ったけれど、私はこの分野の知識はほぼゼロなので、余り役には立たないかも。

Mastodonオープンソースです。オープンソースのよいところはいろいろありますが、そのうちの一つに「気に入らなければ勝手に改造できる」というのがあります。

Mastodonのタイムラインは(Twitterと違って)時系列順に並んでいますが、(Twitterと同じく)相対時間表示になっています。これは気に入らないので改造していきます。

相対時間表示の代わりに絶対時間表示にする

Mastodonの規模はそれほど大きいわけではありませんが、ソースコードを一から読んでいくというのはそれなりに厳しそうです。 そもそも、この分野の知識はほとんどないので、その勉強から始めるとなると気が遠くなります。 そこで、いろいろ眺めてみて、どこが変更に必要そうなところかを探っていきます。

htmlのソースコードを見ていくと、datetime="2018-08-17T22:42:51.543Z"のようなものが入っていることが分かります。それから、5分前みたいなのは普通に書かれていることが分かります。少し待っていると6分前に変わったので、きっとJavascriptでこの辺が操作されているということが推測できます。

そのJavascriptファイルを探し当てたいところですが、どうやら難読化(というより短縮化)がかかっているようで、元のファイルを探す必要がありそうです。

datetimegrepをかけてみれば、きっとその辺のソースコードが見つかるはずです。……とやってみると、かなりの数が引っかかってしまいました。ダメもとでtime datetimeで検索してみると……うまくいきました。app/javascript/mastodon/components/relative_timestamp.jsといういかにもそれらしいファイルが見つかります。実際、中を見てみるとまさにこれが求めるものだということがすぐにわかります。

改造すべき場所はわかりましたが、どうやれば絶対時間を表示できるかの問題を解決していかないといけません。そもそも、文字列をどのように扱うのかがいまいちわからない……。

幸いヒントがあります。七日より前の場合、相対時間表示ではなく絶対時間表示にするようなコードが書かれているので、そこをまねすればよさそうです。

{ ...obj, tag: val }という構文が見慣れないですが、使われ方から見て、「objの要素はそのまま、tag: valを追加する」くらいの意味合いで使われていそうです。よくわかりませんがまねして書いてみます。

(どうやらECMAScript 2018で追加された新機能で、スプレッド構文というらしい。参考文献: Mastodon フロントエンド改造入門 #thebossblog こういう構文に関する情報を無から探し当てるのは初学者には無理があるし、やはり一から学ぶしかないのでしょうか……。)

とはいっても相対時間表示はそれなりに役立つので併記することにしましょう。できあがったコミットがこちら→

Add absolute time · lpha-z/mastodon@b6e2638 · GitHub

(8月18日)のように無駄にかっこがついてしまう問題がありますが、大きな問題ではないし、文字列の扱い方がよくわからないのでしょうがない。今度勉強して直しておきましょう。

コンパイル

ブラウザに流れ込んできているJavascriptコンパイル済みのものになっているので、再コンパイルが必要です。インスタンスを立ち上げた時のウィザードをもう一度やり直せばOKです。そんな遠回りをしなくても、RAILS_ENV=production bundle exec rails assets:precompileを行えばよいです。これをどう見つけるかという話ですが、ウィザードに書いてあります。また、デーモンを再起動(stopしてからstart)する必要があります。

相対時間表示を詳細化する

先々週くらいに言っていた、「1時間前、って書かれても幅が広すぎてわかりづらい!」という文句を改善していきたいと思います。

ソースファイルは、先と同じrelative_timestamp.jsです。そもそも、このソースファイルの中には、5分前という表示ができるだけの情報がないように思われます。「分前」という文字列がどこにもないです。 きっと多言語対応のために別のファイルに定義があるのだと推測します。実際、分前grepすると、app/javascript/mastodon/locales/ja.jsonが引っかかります。どうやらさっきの推測は正しかったようです。

あとは簡単で、適当に修正すればうまくいくはずです。できあがったコミットがこちら↓

Detail relative time (Japanese only) · lpha-z/mastodon@42be44b · GitHub

実はこれはダメ

これでうまくいったと思っていましたが、相対時間表示が1時間0分で止まってしまいました。実は相対時間部分の更新を行うJavascriptは常に動いているわけではなくて、定期的に活動しているようです。で、その更新頻度が1時間に一回になっているため、1時間0分で止まってしまっているようです。UnitDelayって最初何のことかと思っていましたが更新頻度のことだったようです。というか、後から見てみれば_scheduleNextUpdateの中で使用されているし、明らかに更新頻度の定義です(この関数は意味がよくわからず最初読み飛ばしていました……)。

というわけで修正したコミットがこちら↓

Increase frequency of updating relative time · lpha-z/mastodon@a317ef9 · GitHub

reblog(ブースト)の時刻を表示する

これも重要な情報だと私は思うのですが、どうやら表示されていないようです。これも追加したいところですが、今までの手法だけでは太刀打ちできなさそうです。 つまり、今までの二つの変更は情報の表示方法の問題だったのに対し、今回行いたい変更は、新たに情報を持ってくる必要があります。 とはいえ、まずはどこを改造するかを探し当てるところからです。

まず、htmlを眺めているとstatus__wrapperがキーワードになっているようなので、これでgrepします。運よくapp/javascript/mastodon/components/status.jsの一件だけが見つかったのでこれでまず間違いないでしょう。

しかし、肝心のreblogされた時刻はこのソースファイル中では使う予定がないため、適当な変数に代入されている様子がありません。おそらくデータとしてはあるはずですが(そうでないと時系列順にならばないはずです)、オブジェクトのどこに格納されているかが分かりません。 型なし言語はこういう時に困ります……。

このソースファイルのthis.props(なのかな?)に渡されているオブジェクトを構築している部分を探し当てる必要があります。 あるいは、ブラウザでデバッグコンソールにダンプしてもよさそうです。

私の場合は、上記手段のやり方がよくわからなかったため、このソースコードをじっくり眺めました。その結果、

  • status.get('reblog')はreblogだったときに限って、そのreblogしたトゥートが入っている
  • status.get('account')は、
    • 普通のトゥートの時は、そのトゥートをしたアカウントの情報が入っている
    • reblogの時は、reblogしたアカウントの情報が入っている(reblogしたトゥートをしたアカウントの情報ではない)

ということが読み取れます(92, 93行目で変数の上書きを行っているところ)。

また、トゥートを行った時刻は、<RelativeTimestamp timestamp={status.get('created_at')} />という形でRelativeTimestampに渡されていることも読み取れます。 つまり、reblog元のトゥートの作成時間は、status.get('reblog').get('created_at')に入っています。

時刻を格納する変数は一つしかなくて、そこにはトゥート時刻が収まっているためreblog時刻はオブジェクトの中には入っていない、といったパターンも考えられましたが、そうなっている可能性は低そうであることが分かりました。

そこで、status.get('created_at')にreblog時刻が入っていると信じてやってみると……。うまくいきました。

できあがったコミットはこちら↓

Show reblog time (Japanese only) · lpha-z/mastodon@696fafd · GitHub

ふぁぼられた時間を表示する

Twitterにもあるし、これくらいの表示はしたいところです。

app/javascript/mastodon/locales/ja.jsonから逆算して、notification.favouriteなどが含まれているソースファイルを調べてみると、app/javascript/mastodon/features/notifications/components/notification.jsが該当することが分かります。

まず先ほどと同じように<RelativeTimestamp timestamp={notification.get('created_at')} />などとやってみると、コンパイルでエラーになります。 これは当然で、import RelativeTimestamp from '../../../components/relative_timestamp';がないといけないということはすぐにわかります。

この修正を行えばコンパイル自体は通るのですが、ブラウザ側でエラーになります。やはりcreated_at要素がないようです。

やはりこのソースファイルにわたってくるオブジェクトを作成している、上位のソースファイルを探す必要がありそうです。

さんざんいろんなソースファイルを探し回った挙句、app/javascript/mastodon/reducers/notifications.jsを発見しました。

加える変更はたった一行ですが探すのが非常に大変でした……。

できあがったコミットはこちら↓

Show notification time (Japanese only) · lpha-z/mastodon@a74d261 · GitHub

おわりに

さっぱり知識がない状態で、いかに改造すべきポイントを探し当てて改造していくのかという過程を紹介してみました。

みんなもMastodonインスタンスを立てて改造してみましょう。