2022-01-05 17:53:44 +00:00
local st = require " util.stanza " ;
local http = require " net.http " ;
2022-01-05 18:17:40 +00:00
local gettime = require ' socket ' . gettime ;
2022-01-05 17:53:44 +00:00
local async = require " util.async " ;
local b64 = require " util.encodings " . base64.encode ;
local jid_split = require " util.jid " . split ;
local json = require " util.json " ;
local uh = require " util.http " ;
module : add_feature ( " vcard-temp " ) ;
2022-01-05 18:17:40 +00:00
local CACHE_EXPIRY = 3600 ;
local cache_user = { } ;
2022-01-05 17:53:44 +00:00
local peertube_url = assert ( module : get_option_string ( " peertubelivechat_vcard_peertube_url " , nil ) , " 'peertubelivechat_vcard_peertube_url' is a required option " ) ;
if peertube_url : sub ( - 1 , - 1 ) == " / " then peertube_url = peertube_url : sub ( 1 , - 2 ) ; end
2023-11-16 09:33:16 +00:00
local function get_avatar_url ( ret )
-- Note:
-- * before Peertube v6.0.0: using ret.avatar
-- * after Peertube v6.0.0: using ret.avatars, searching for width 48, or for the smallest width
if ret.avatar and ret.avatar . path then
module : log ( " debug " , " User avatar path (Peertube < v6): %s " , peertube_url .. ret.avatar . path ) ;
return peertube_url .. ret.avatar . path ;
end
local min_width = 100000 ;
local min_path = nil ;
if ret.avatars and type ( ret.avatars ) == " table " then
for _ , avatar in ipairs ( ret.avatars ) do
if avatar.path and avatar.width then
if ( avatar.width == 48 ) then
module : log ( " debug " , " User avatar path (Peertube >= v6, width 48): %s " , peertube_url .. avatar.path ) ;
return peertube_url .. avatar.path ;
end
if ( avatar.width < min_width ) then ;
min_path = avatar.path ;
min_width = avatar.width ;
end
end
end
if min_path then
module : log ( " debug " , " User avatar path (Peertube >= v6, minimal width): %s " , peertube_url .. min_path ) ;
return peertube_url .. min_path ;
end
end
module : log ( " debug " , " Cant find user avatar url " ) ;
return nil ;
end
2022-01-05 17:53:44 +00:00
module : hook ( " iq-get/bare/vcard-temp:vCard " , function ( event )
local origin , stanza = event.origin , event.stanza ;
local who = jid_split ( stanza.attr . to ) or origin.username
module : log ( " debug " , " vCard request for %s " , who ) ;
2022-01-05 18:17:40 +00:00
local from_cache = cache_user [ who ] ;
if from_cache then
if from_cache [ " last_fetch_time " ] and from_cache [ " last_fetch_time " ] + CACHE_EXPIRY < gettime ( ) then
module : log ( " debug " , " vCard result for %s was in cache but is expired. " , who ) ;
cache_user [ who ] = nil
else
module : log ( " debug " , " vCard result for %s is in cache. " , who ) ;
if ( from_cache [ ' vcard ' ] ) then
origin.send ( st.reply ( stanza ) : add_child ( from_cache [ " vcard " ] ) ) ;
else
origin.send ( st.error_reply ( stanza , " cancel " , " item-not-found " ) ) ;
end
return true ;
end
else
module : log ( " debug " , " vCard result for %s is not in cache. " , who ) ;
end
2022-01-05 17:53:44 +00:00
local wait , done = async.waiter ( ) ;
local url = peertube_url .. ' /api/v1/accounts/ ' .. uh.urlencode ( who ) ;
module : log ( " debug " , " Calling Peertube API: %s " , url ) ;
local ret ;
http.request ( url , { accept = " application/json " } , function ( body , code )
if math.floor ( code / 100 ) == 2 then
local parsed , parse_err = json.decode ( body ) ;
if not parsed then
module : log ( " debug " , " Got invalid JSON from %s: %s " , url , parse_err ) ;
else
ret = parsed ;
end
else
module : log ( " debug " , " Rejected by API: " , body ) ;
end
done ( ) ;
end )
wait ( ) ;
if not ret then
module : log ( " debug " , " Peertube user not found, no vCard for %s " , who ) ;
origin.send ( st.error_reply ( stanza , " cancel " , " item-not-found " ) ) ;
2022-01-05 18:17:40 +00:00
cache_user [ who ] = { last_fetch_time = gettime ( ) } ;
2022-01-05 17:53:44 +00:00
return true ;
end
2022-01-05 19:13:59 +00:00
2022-01-05 17:53:44 +00:00
local vcard_temp = st.stanza ( " vCard " , { xmlns = " vcard-temp " } ) ;
2022-01-05 18:23:20 +00:00
vcard_temp : text_tag ( " FN " , ret.displayName ) ;
2022-01-05 17:53:44 +00:00
vcard_temp : text_tag ( " NICKNAME " , ret.displayName ) ;
2022-01-05 19:11:22 +00:00
vcard_temp : text_tag ( " URL " , ret.url ) ;
2022-01-05 19:13:59 +00:00
2023-11-16 09:33:16 +00:00
local avatar_url = get_avatar_url ( ret ) ;
if avatar_url then
-- module:log("debug", "Downloading user avatar on %s", avatar_url);
2022-01-05 17:53:44 +00:00
local waitAvatar , doneAvatar = async.waiter ( ) ;
2023-11-16 09:33:16 +00:00
http.request ( avatar_url , { } , function ( body , code , response )
2022-01-05 17:53:44 +00:00
if math.floor ( code / 100 ) == 2 then
module : log ( " debug " , " Avatar found for %s " , who ) ;
2022-01-06 02:29:52 +00:00
vcard_temp : tag ( " PHOTO " ) ;
2022-01-05 19:11:22 +00:00
if ( response and response.headers and response.headers [ " content-type " ] ) then
module : log ( " debug " , " Avatar content-type: %s " , response.headers [ " content-type " ] ) ;
2022-01-06 02:29:52 +00:00
vcard_temp : text_tag ( " TYPE " , response.headers [ " content-type " ] ) ;
vcard_temp : text_tag ( " BINVAL " , b64 ( body ) ) ;
vcard_temp : up ( ) ;
2022-01-05 19:11:22 +00:00
else
module : log ( " debug " , " Avatar has no content-type. " ) ;
2022-01-05 17:53:44 +00:00
end
else
module : log ( " debug " , " Cant load avatar: " , body ) ;
end
doneAvatar ( ) ;
end )
waitAvatar ( ) ;
end
origin.send ( st.reply ( stanza ) : add_child ( vcard_temp ) ) ;
2022-01-05 18:17:40 +00:00
cache_user [ who ] = { last_fetch_time = gettime ( ) , vcard = vcard_temp } ;
2022-01-05 17:53:44 +00:00
return true ;
2022-01-05 19:13:59 +00:00
end ) ;