【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

has_one_attached :image

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

view

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

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

controller

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を使うのに役立ちそうです。今後も学んでいきたいと思います。