Add job for storing/announcing avatar in ejabberd
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Râu Cao 2025-05-15 19:49:08 +04:00
parent 382c5ad10e
commit 2c00702aa9
Signed by: raucao
GPG Key ID: 37036C356E56CC51
2 changed files with 107 additions and 14 deletions

View File

@ -0,0 +1,97 @@
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|
res = ejabberd.send_stanza {
from: user.address, to: user.address, stanza: stanza
}
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)
[
"""
<iq type='set' from='#{@user.address}' id='avatar-data-#{rand(101)}'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<publish node='urn:xmpp:avatar:data'>
<item id='#{sha1_hash}'>
<data xmlns='urn:xmpp:avatar:data'>#{base64_data}</data>
</item>
</publish>
</pubsub>
</iq>
""".strip,
"""
<iq type='set' from='#{@user.address}' id='avatar-metadata-#{rand(101)}'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<publish node='urn:xmpp:avatar:metadata'>
<item id='#{sha1_hash}'>
<metadata xmlns='urn:xmpp:avatar:metadata'>
<info bytes='#{img_data.size}'
id='#{sha1_hash}'
height='256'
type='image/png'
width='256'/>
</metadata>
</item>
</publish>
</pubsub>
</iq>
""".strip,
]
end
end

View File

@ -4,16 +4,8 @@ class EjabberdApiClient
end end
def post(endpoint, payload) def post(endpoint, payload)
res = Faraday.post("#{@base_url}/#{endpoint}", payload.to_json, Faraday.post "#{@base_url}/#{endpoint}", payload.to_json,
"Content-Type" => "application/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
end end
# #
@ -24,6 +16,14 @@ class EjabberdApiClient
post "add_rosteritem", payload post "add_rosteritem", payload
end 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) def get_vcard2(user, name, subname)
payload = { payload = {
user: user.cn, host: user.ou, user: user.cn, host: user.ou,
@ -47,8 +47,4 @@ class EjabberdApiClient
} }
post "private_set", payload post "private_set", payload
end end
def send_message(payload)
post "send_message", payload
end
end end