Files
redux-scraper/app/lib/domain/fa/parser/user_page_helper.rb
2025-01-01 03:29:53 +00:00

275 lines
6.7 KiB
Ruby

# typed: false
class Domain::Fa::Parser::UserPageHelper < Domain::Fa::Parser::Base
VERSION_0 = Domain::Fa::Parser::Page::VERSION_0
VERSION_1 = Domain::Fa::Parser::Page::VERSION_1
VERSION_2 = Domain::Fa::Parser::Page::VERSION_2
def initialize(elem, page_version)
@elem = elem
@page_version = page_version
end
# https://webcache.googleusercontent.com/search?q=cache:L85loB7VJXwJ:https://forums.furaffinity.net/threads/question-about-the-username-prefix.1655691/&cd=10&hl=en&ct=clnk&gl=us
PREFIXES = [
"~", # regular user
"!", # suspended
"-", # banned (permanent)
"@", # admin
"", # deceased
]
def name
@name ||=
begin
elem =
case @page_version
when VERSION_0, VERSION_1
elem = @elem.css(".addpad.lead b")
when VERSION_2
@elem.css("userpage-nav-user-details username")
else
unimplemented_version!
end
name = elem&.first&.text&.strip
if name
if @elem.css("userpage-nav-header img.userIcon.type-admin").first
# in newer versions of FA user pages, admins have no '@' prefix,
# but rather an indicator image icon
if PREFIXES.include?(name[0])
raise("invalid prefix for admin user name: #{name}")
end
name
elsif PREFIXES.include?(name[0])
name[1..]
else
if @elem.css("img.userIcon.faplus-icon")
# FA+ users have no prefix
name
else
raise("invalid prefix for name: #{name}")
end
end
end
end
end
def account_status
@account_status ||=
begin
if @elem.css("userpage-nav-header img.userIcon.type-admin").first
:admin
else
elem =
case @page_version
when VERSION_2
@elem.css("userpage-nav-user-details username")
else
unimplemented_version!
end
name = elem&.first&.text&.strip || ""
case name[0]
when "~"
:active
when "!"
:suspended
when "-"
:banned
when ""
:deceased
else
nil
end
end
end
end
def full_name
@full_name ||= elem_after_text_match(main_about.children, /Full/).text.strip
end
def artist_type
@artist_type ||=
elem_after_text_match(main_about.children, /Type/).try(:text).try(:strip)
end
def profile_thumb_url
@profile_thumb_url ||=
case @page_version
when VERSION_0
@elem.css(".addpad.alt1 a img.avatar").first.try(:[], "src")
when VERSION_2
@elem.css("userpage-nav-avatar a.current img").first.try(:[], "src")
else
unimplemented_version!
end
end
def registered_since
@registered_since ||=
case @page_version
when VERSION_0, VERSION_1
elem_after_text_match(main_about.children, /Registered/).text.strip
when VERSION_2
date_str =
@elem
.css("username span")
.find { |elem| elem&.text&.strip == "Registered:" }
&.next_sibling
&.text
&.strip
DateTime.parse(date_str) if date_str
else
unimplemented_version!
end
end
def mood
@mood ||= elem_after_text_match(main_about.children, /mood/).text.strip
end
def profile_html
@profile_html ||= main_about.inner_html.force_encoding("utf-8")
end
def num_pageviews
@num_pageviews ||= stat_value(:pvs, 0)
end
def num_submissions
@num_submissions ||= stat_value(:subs, 1)
end
def num_comments_recieved
@num_comments_recieved ||= stat_value(:crec, 3)
end
def num_comments_given
@num_comments_given ||= stat_value(:cgiv, 4)
end
def num_journals
@num_journals ||= stat_value(:njr, 5)
end
def num_favorites
@num_favorites ||= stat_value(:nfav, 2)
end
def recent_fav_fa_ids
@recent_favs ||=
case @page_version
when VERSION_2
@elem
.css("#gallery-latest-favorites")
.first
&.css("figure a")
&.map do |elem|
href = elem["href"]
%r{/view/(\d+)}.match(href)[1]&.to_i ||
raise("invalid url: #{href}")
end || []
else
unimplemented_version!
end
end
RecentUser =
Struct.new(:name, :url_name) do
def to_a
[name, url_name]
end
end
def recent_watchers
@recent_watchers ||= recent_users_for_section("Recent Watchers")
end
def recent_watching
@recent_watching ||= recent_users_for_section("Recently Watched")
end
private
def recent_users_for_section(section_name)
case @page_version
when VERSION_2
section_elem =
@elem
.css(".userpage-section-left")
.find do |elem|
elem.css(".section-header h2")&.first&.text&.strip == section_name
end
section_elem = section_elem.css(".section-body").first
section_elem
.css("a")
.map do |link_elem|
href = link_elem["href"]
url_name =
%r{/user/(.+)/}.match(href)&.[](1) || raise("invalid url: #{href}")
name = link_elem.css(".artist_name").first.text.strip
RecentUser.new(name, url_name)
end
else
unimplemented_version!
end
end
def stat_value(legacy_name, redux_idx)
legacy_map =
if false # old mode?
{ pvs: 2, subs: 5, crec: 8, cgiv: 11, njr: 14, nfav: 17 }
else
{ pvs: 2, subs: 6, crec: 10, cgiv: 14, njr: 18, nfav: 22 }
end
value =
case @page_version
when VERSION_0, VERSION_1
statistics.children[legacy_map[legacy_name] || raise].text.strip.to_i
when VERSION_2
statistics.css(".highlight")[redux_idx]&.next_sibling&.text&.strip&.to_i
else
unimplemented_version!
end
# FA databases can be a little weird
if value >= (2**32 - 1)
0
else
value
end
end
def statistics
@statistics ||=
case @page_version
when VERSION_0, VERSION_1
@elem.css(".ldot table tr:last-child td").first
when VERSION_2
@elem
.css(".userpage-layout-right-col-content .userpage-section-right")
.find do |child|
child.css(".section-header h2")&.first&.text&.strip == "Stats"
end
else
unimplemented_version!
end
end
def main_about
@main_about ||=
case @page_version
when VERSION_0, VERSION_1
@elem.css(".alt1.addpad .ldot").first
when VERSION_2
@elem.css(".section-body.userpage-profile").first
else
unimplemented_version!
end
end
end