diff --git a/app/jobs/xmpp_set_avatar_job.rb b/app/jobs/xmpp_set_avatar_job.rb new file mode 100644 index 0000000..b5bc1f0 --- /dev/null +++ b/app/jobs/xmpp_set_avatar_job.rb @@ -0,0 +1,95 @@ +require 'digest' +require "image_processing/vips" + +class XmppSetAvatarJob < ApplicationJob + queue_as :default + + def perform(user:, overwrite: false) + @user = user + + unless overwrite + current_avatar = get_current_avatar + Rails.logger.debug { "User #{user.cn} already has an avatar set. Nothing to do." } + return if current_avatar.present? + end + + Rails.logger.debug { "Setting XMPP avatar for user #{user.cn}" } + + stanzas = build_xep0084_stanzas + + stanzas.each do |stanza| + payload = { from: @user.address, to: @user.address, stanza: stanza } + res = ejabberd.send_stanza payload + raise res.inspect if res.status != 200 + end + end + + private + + def ejabberd + @ejabberd ||= EjabberdApiClient.new + end + + def get_current_avatar + res = ejabberd.get_vcard2 @user, "PHOTO", "BINVAL" + + if res.status == 200 + # VCARD PHOTO/BINVAL prop exists + res.body + elsif res.status == 400 + # VCARD or PHOTO/BINVAL prop does not exist + nil + else + # Unexpected error, let job fail + raise res.inspect + end + end + + def process_avatar + @user.avatar.blob.open do |file| + processed = ImageProcessing::Vips + .source(file) + .resize_to_fill(256, 256) + .convert("png") + .call + processed.read + end + end + + def build_xep0084_stanzas + img_data = process_avatar + sha1_hash = Digest::SHA1.hexdigest(img_data) + base64_data = Base64.strict_encode64(img_data) + + [ + """ + + + + + #{base64_data} + + + + + """.strip, + """ + + + + + + + + + + + + """.strip, + ] + end +end diff --git a/app/services/ejabberd_api_client.rb b/app/services/ejabberd_api_client.rb index a002d7f..df22d25 100644 --- a/app/services/ejabberd_api_client.rb +++ b/app/services/ejabberd_api_client.rb @@ -4,16 +4,8 @@ class EjabberdApiClient end def post(endpoint, payload) - res = Faraday.post("#{@base_url}/#{endpoint}", payload.to_json, - "Content-Type" => "application/json") - - if res.status != 200 - #TODO Send custom event to Sentry - Rails.logger.error "[ejabberd] API request failed:" - Rails.logger.error res.body - end - - res + Faraday.post "#{@base_url}/#{endpoint}", payload.to_json, + "Content-Type" => "application/json" end # @@ -24,6 +16,14 @@ class EjabberdApiClient post "add_rosteritem", payload end + def send_message(payload) + post "send_message", payload + end + + def send_stanza(payload) + post "send_stanza", payload + end + def get_vcard2(user, name, subname) payload = { user: user.cn, host: user.ou, @@ -47,8 +47,4 @@ class EjabberdApiClient } post "private_set", payload end - - def send_message(payload) - post "send_message", payload - end end