Mastodonを読む

はじめに

前回は、クライアント(画面)でフォローボタンを押したときにサーバに処理が飛んでいくところまでを確認しました。今回は引き続き、サーバ側の処理を見ていきます。

app/controllers/api/v1/accounts_controller.rb

フォローボタンが押されると、サーバ側の「/api/v1/accounts/:id/follow」が呼び出されます。routes.rbを見てみると他にもフォロー処理をしている雰囲気のものがありますがパス的にはAccountsControllerのfollowメソッドが呼び出されるはずです。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
-
|
|
|
!
  def follow
    FollowService.new.call(current_user.account, @account.acct)
    set_relationship
    render :relationship
  end

@accountはbefore_actionを使用して設定されています。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
 
 
-
|
!
  before_action :set_account, except: [:verify_credentials, :update_credentials, :suggestions, :search]
 
  def set_account
    @account = Account.find(params[:id])
  end

app/models/account.rb

Accountのacctはインスタンスも含めてアカウントを識別するための情報のようです。(domainはモデルの属性)

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
-
|
!
 
-
|
!
  def local?
    domain.nil?
  end
 
  def acct
    local? ? username : "#{username}@#{domain}"
  end

さて、ここで気づくことがあります。上のコードを考えると、別インスタンスにいるアカウントについてもaccountsテーブルに情報が格納されているということになります。 では、どうやって別インスタンスのアカウント情報を取ってくるのか?という疑問はありますが、今はaccountsテーブルにすでに情報があるという前提で先に進みましょう。

app/services/follow_service.rb

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
-
|
|
-
|
|
-
|
|
|
|
|
-
|
|
|
!
!
class FollowService < BaseService
  include StreamEntryRenderer
 
  # Follow a remote user, notify remote user about the follow
  # @param [Account] source_account From which to follow
  # @param [String] uri User URI to follow in the form of username@domain
  def call(source_account, uri)
    target_account = FollowRemoteAccountService.new.call(uri)
 
    raise ActiveRecord::RecordNotFound if target_account.nil? || target_account.id == source_account.id || target_account.suspended?
    raise Mastodon::NotPermittedError  if target_account.blocking?(source_account) || source_account.blocking?(target_account)
 
    if target_account.locked?
      request_follow(source_account, target_account)
    else
      direct_follow(source_account, target_account)
    end
  end

FollowRemoteAccountServiceはaccountsテーブルにアカウントがあればそれを返す、ない場合はWebFingerを利用して指定されたドメインから情報を取得→Account作成→返すということをしています。これはこれでおもしろいですが話が発散してしまうので今回は省略。

といいつつ一言だけ。WebFingerでは特定のURLにアクセスすることでユーザの情報を取得するようです。MastodonではGoldFingerというgemを使って、って、中の人一緒ですね(笑) 以下のURLへのアクセスが行われ、情報が返されるようです。

Everything is expanded.Everything is shortened.
  1
 
  get '.well-known/webfinger', to: 'well_known/webfinger#show', as: :webfinger

さて、話を戻して、鍵付きかどうかで処理が分かれていますが、鍵付きでないとしてdirect_followを見てみましょう。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
-
|
|
-
|
|
|
|
|
!
|
|
|
|
!
  def direct_follow(source_account, target_account)
    follow = source_account.follow!(target_account)
 
    if target_account.local?
      NotifyService.new.call(target_account, follow)
    else
      Pubsubhubbub::SubscribeWorker.perform_async(target_account.id) unless target_account.subscribed?
      NotificationWorker.perform_async(build_follow_xml(follow), source_account.id, target_account.id)
      AfterRemoteFollowWorker.perform_async(follow.id)
    end
 
    MergeWorker.perform_async(target_account.id, source_account.id)
 
    follow
  end

フォロー処理を行った後、ローカルアカウントなのかリモートアカウントなのかによって処理が分かれています。全部見ていくと長くなるので各処理の概要だけ挙げると以下のことをしています。

NotifyService
メールの送信、Redisへの書き込み(ストリーミングが有効の場合はクライアント画面に飛んでいく)
Pubsubhubbub::SubscribeWorker
OStatus2を使いSubscriptionのリクエスト送信。相手側は「/api/push」で受け取ってSubscriptionを作成
NotificationWorker
Salmon経由でフォローを通知。受け取った相手インスタンスはフォローの処理を行う(呼ばれるのは、/api/salmon/:id」)
AfterRemoteFollowWorker
よくわからない。時間差で相手が鍵垢にしたときのやり直し用?
MergeWorker
フォローしたアカウントのトゥートを自分のホームに表示されるようにする処理(Redisへの書き込み)

おわりに

今回はフォロー時のサーバ側の処理について見てきました。リモートの場合はいろいろ端折りましたが、WebFinger、PubSubHubbub、Salmonを利用してフォロー対象のアカウントがいるインスタンスとの通信が行われていました。フォローの流れがわかったので次回はリモートフォローについて詳細に見ていきたいと思います。


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-05-14 (日) 21:39:03 (2765d)