【Rails】自作WebアプリからMastodonのプロフィール画像を変更


個人開発のWebアプリまちかどルートv5.41への実装メモです。

プログラミングに入門して8ヶ月。
今回も SNS「Mastodon」のAPI に挑戦してみました。

まえがき

・まちかどルートではMastodonのアカウントでログイン認証するようにしています。

・今回の実装をするまでは、まちかどルートのプロフィール画像を変えるためにわざわざMastodonで変更し、まちかどルートで再ログインする必要がありました。

・画像アップローダーとしてRails 5.2の新機能 Active Storage を使い、ファイルをAmazon S3に保管しているのですがgem 'mastodon-api'(v2.0)を通してアップロードするとき、その環境のせいでとても苦労しました。

model

user.rb
has_one_attached :image

Active Storageを使うのでマニュアルどおりの作法でmodelにこう書きます。バリデーションについては今回、割愛します。

view

edit.html.erb
<%= form_with model: @user, multipart: true, local: true do |f| %>
  <%= f.file_field :image %>
<%= button_tag :type => "submit" do %>変更を保存<% end %>

プロフィール画像のファイルを選択するためのフォームです。

controller

users_controller.rb
def update

   # 画像の選択の有無を確認
   if params[:image] != nil

    # 画像を uploaded_file に格納
    uploaded_file = params[:image]

    # 画像を /public にいったん配置するため output_path にパスを設定
    output_path = Rails.root.join('public', uploaded_file.original_filename)


    # Mastodonには2MB制限があるので画像を縮小

    ## MiniMagickを使います。まずは画像を入力
    img = MiniMagick::Image.read(uploaded_file)

    ## 縮小します
    img.resize "300x300"

    ## 縮小したら /public に書き出します
    img.write output_path


    # MastodonのAPIを通してアップロードします

    ## 配列を用意します。Mastodon指定のパラメーターは avatar です
    user_array = { "avatar": output_path }

    ## APIを叩くためのクライアントを生成します
    domain = '[対象のMastodonインスタンスのドメイン]'
    access_token = '[ユーザーのアクセストークン]'
    client = Mastodon::REST::Client.new(base_url: "https://#{domain}", bearer_token: access_token)

    ## MastodonのAPIを叩きます
    result = client.update_credentials(user_array)


    # 以上でMastodon側のプロフィール画像が変更されます
    # 続いて、まちかどルートにも同じ画像を反映させます
    @user.avatar = result.avatar
    @user.save


    # 最後に、不要となった/publicの画像を削除します
    File.delete(output_path)

   end

   flash[:notice] = "アイコンを変更しました" 
   redirect_to @user
end

controllerが一番苦労しました。
解説はコメントアウトにある通りです。

あとがき

Active Storageはとても簡単に導入できるアップローダーなのですが

user_array = { "avatar": @user.image }

と書ければ、わざわざ/publicに画像を配置する手間がなくて楽なのに url_for(@user.image)とやっても「そんな画像はありません」というエラーが返ってきてしまうんです。

というわけで、いろいろ試行錯誤して上記のようになりました。とくに/publicにいったん配置する方法がわかったので、これから他の画像系APIを使うのに役立ちそうです。今後も学んでいきたいと思います。