Files
redux-scraper/app/lib/bluesky/process_post_helper.rb
2025-08-17 18:51:55 +00:00

151 lines
4.1 KiB
Ruby

# typed: strict
# frozen_string_literal: true
#
class Bluesky::ProcessPostHelper
extend T::Sig
include HasColorLogger
IMAGE_OR_VIDEO =
T.let(%w[app.bsky.embed.images app.bsky.embed.video], T::Array[String])
sig { params(deferred_job_sink: DeferredJobSink).void }
def initialize(deferred_job_sink)
@deferred_job_sink = deferred_job_sink
end
sig { params(did: String, cid: String).returns(String) }
def self.construct_blob_url(did, cid)
"https://bsky.social/xrpc/com.atproto.sync.getBlob?did=#{did}&cid=#{cid}"
end
sig { params(embed_data: T::Hash[String, T.untyped]).returns(T::Boolean) }
def should_process_post?(embed_data)
type = embed_data["$type"]
if IMAGE_OR_VIDEO.include?(type)
true
elsif type == "app.bsky.embed.recordWithMedia"
embed_type = embed_data.dig("media", "$type")
IMAGE_OR_VIDEO.include?(embed_type)
else
false
end
end
sig do
params(
post: Domain::Post::BlueskyPost,
embed_data: T::Hash[String, T.untyped],
did: String,
).void
end
def process_post_media(post, embed_data, did)
case embed_data["$type"]
when "app.bsky.embed.images"
process_post_images(post, embed_data, did)
when "app.bsky.embed.video"
process_post_video(post, embed_data, did)
when "app.bsky.embed.recordWithMedia"
embed_type = embed_data.dig("media", "$type")
if embed_type == "app.bsky.embed.images"
process_post_images(post, embed_data["media"], did)
elsif embed_type == "app.bsky.embed.video"
process_post_video(post, embed_data["media"], did)
end
end
end
sig do
params(
post: Domain::Post::BlueskyPost,
embed_data: T::Hash[String, T.untyped],
did: String,
).void
end
def process_post_images(post, embed_data, did)
images = embed_data.dig("images") || []
images.each_with_index do |image_data, index|
post_file = post.files.build(file_order: index)
set_blob_ref_and_url(post_file, image_data["image"], did)
set_aspect_ratio(post_file, image_data["aspectRatio"])
set_alt_text(post_file, image_data["alt"])
post_file.save!
@deferred_job_sink.defer_job(
Domain::StaticFileJob,
{ post_file: },
{ queue: "bluesky" },
)
logger.debug(
format_tags(
"created image for post",
make_tags(at_uri: post.at_uri, post_file_id: post_file.id),
),
)
end
end
sig do
params(
post: Domain::Post::BlueskyPost,
embed_data: T::Hash[String, T.untyped],
did: String,
).void
end
def process_post_video(post, embed_data, did)
post_file = post.files.build(file_order: 0)
set_blob_ref_and_url(post_file, embed_data["video"], did)
set_aspect_ratio(post_file, embed_data["aspectRatio"])
set_alt_text(post_file, embed_data["alt"])
post_file.save!
@deferred_job_sink.defer_job(
Domain::StaticFileJob,
{ post_file: },
{ queue: "bluesky" },
)
logger.debug(
format_tags(
"created video for post",
make_tags(at_uri: post.at_uri, post_file_id: post_file.id),
),
)
end
sig do
params(
post_file: Domain::PostFile::BlueskyPostFile,
file_data: T::Hash[String, T.untyped],
did: String,
).void
end
def set_blob_ref_and_url(post_file, file_data, did)
return unless file_data.dig("$type") == "blob"
blob_ref = file_data.dig("ref", "$link")
return unless blob_ref
post_file.blob_ref = blob_ref
post_file.url_str = self.class.construct_blob_url(did, blob_ref)
end
sig do
params(
post_file: Domain::PostFile::BlueskyPostFile,
aspect_ratio: T.nilable(T::Hash[String, T.untyped]),
).void
end
def set_aspect_ratio(post_file, aspect_ratio)
return unless aspect_ratio
post_file.aspect_ratio_width = aspect_ratio.dig("width")
post_file.aspect_ratio_height = aspect_ratio.dig("height")
end
sig do
params(
post_file: Domain::PostFile::BlueskyPostFile,
alt_text: T.nilable(String),
).void
end
def set_alt_text(post_file, alt_text)
post_file.alt_text = alt_text if alt_text
end
end