module FandomTrivia
Constants
- ATHF_WIKI_HOST
- CATEGORY_MEMBER_LIMIT
- CHARACTER_CATEGORIES
- EPISODE_CATEGORY
- MAX_ATTEMPTS
- Round
- SOURCE_TYPES
- TRIVIA_SECTION_PATTERN
- USER_AGENT
Public Instance Methods
Source
# File lib/fandom_trivia.rb, line 252 def answer_aliases(answer, page_title:, source_type:) aliases = [] aliases.concat(quoted_strings(answer)) if answer.include?('"') normalized = answer.to_s.strip aliases << normalized aliases << normalized.split.last if normalized.include?(" ") if source_type == :character aliases << page_title.to_s aliases << page_title.to_s.split.last end if source_type == :episode aliases << page_title.to_s.sub(/\s*\(episode\)\z/i, "") end aliases.map { |value| value.to_s.strip.downcase }.reject { |value| value.length < 2 }.uniq end
Source
# File lib/fandom_trivia.rb, line 80 def api_url(base_url, params) uri = URI.parse(base_url) uri.path = "/api.php" uri.query = URI.encode_www_form(params.merge(format: "json", utf8: "1")) uri.to_s end
Source
# File lib/fandom_trivia.rb, line 268 def build_round_from_fact(fact, page_title:, source_type:) cleaned = clean_wiki_markup(fact) return nil if cleaned.length < 20 quotes = quoted_strings(cleaned) answer = quotes.find { |value| value.length >= 3 } if answer question = cleaned.sub(/"#{Regexp.escape(answer)}"/, "___") question = "Fill in the blank: #{question}" elsif (match = cleaned.match(/\bfrom ([A-Z][^.]+?)(?:\.|$)/)) answer = match[1].strip question = cleaned.sub(answer, "___") question = "Fill in the blank: #{question}" else candidates = cleaned.scan(/\b[A-Z][A-Za-z0-9'']+(?:\s+[A-Z][A-Za-z0-9'']+){0,4}\b/) answer = candidates.max_by(&:length) return nil if answer.nil? || answer.length < 4 question = cleaned.sub(answer, "___") question = "Fill in the blank: #{question}" end answer = answer.to_s.strip return nil if answer.empty? Round.new( question: truncate(question, 420), answer: answer.downcase, aliases: answer_aliases(answer, page_title: page_title, source_type: source_type), source_page: page_title, source_type: source_type ) end
Source
# File lib/fandom_trivia.rb, line 151 def category_page_titles(base_url, category_title, fetch: method(:default_fetch), limit: CATEGORY_MEMBER_LIMIT) titles = [] continue_token = nil loop do params = { action: "query", generator: "categorymembers", gcmtitle: category_title, gcmtype: "page", gcmlimit: [limit - titles.length, 50].max.clamp(1, 50).to_s } params[:gcmcontinue] = continue_token if continue_token body = fetch_json(api_url(base_url, params), fetch: fetch) pages = body.dig("query", "pages") break unless pages.is_a?(Hash) titles.concat( pages.values.filter_map do |page| title = page["title"].to_s.strip next if title.empty? || title.start_with?("Category:") title end ) break if titles.length >= limit continue_token = body.dig("continue", "gcmcontinue") break if continue_token.nil? || continue_token.empty? end titles rescue StandardError [] end
Source
# File lib/fandom_trivia.rb, line 185 def character_page_titles(base_url, fetch: method(:default_fetch)) CHARACTER_CATEGORIES.each do |category| titles = category_page_titles(base_url, category, fetch: fetch) return titles unless titles.empty? end [] end
Source
# File lib/fandom_trivia.rb, line 53 def character_title(source_url) CharacterTranscripts.page_title_from_url(source_url) end
Source
# File lib/fandom_trivia.rb, line 204 def clean_wiki_markup(text) CGI.unescapeHTML( text.to_s .gsub(/<[^>]+>/, " ") .gsub(/\[\s*\d*\s*\]/, "") .gsub(/\[\s*([^\]|]+)(?:\|[^\]]+)?\s*\]/, '\1') .gsub(/"\s+([^"]+?)\s+"/, '"\1"') .gsub(/[ \t]+/, " ") .gsub(/\n{2,}/, "\n") .strip ) end
Source
# File lib/fandom_trivia.rb, line 87 def default_fetch(url) RabbotHttp.fetch(url, user_agent: USER_AGENT) end
Source
# File lib/fandom_trivia.rb, line 193 def episode_page_titles(source_url, fetch: method(:default_fetch), store: RabbotDb) if wiki_host(source_url) == ATHF_WIKI_HOST AthfEpisodeIndex.episodes(fetch: fetch, store: store).filter_map do |episode| title = episode["title"].to_s.strip title.empty? ? nil : title end else category_page_titles(wiki_base_url(source_url), EPISODE_CATEGORY, fetch: fetch) end end
Source
# File lib/fandom_trivia.rb, line 36 def fandom_source?(profile) profile&.source_url && CharacterTranscripts.fandom_url?(profile.source_url) end
Source
# File lib/fandom_trivia.rb, line 91 def fetch_json(url, fetch: method(:default_fetch)) JSON.parse(fetch.call(url)) end
Source
# File lib/fandom_trivia.rb, line 111 def fetch_page_intro(base_url, page_title, fetch: method(:default_fetch)) url = api_url( base_url, action: "parse", page: page_title, prop: "text", section: "0" ) body = fetch_json(url, fetch: fetch) clean_wiki_markup(body.dig("parse", "text", "*")) rescue StandardError "" end
Source
# File lib/fandom_trivia.rb, line 349 def fetch_round(profile, fetch: method(:default_fetch), rand: ->(max) { Random.rand(0...max) }, store: RabbotDb) return nil unless fandom_source?(profile) base = wiki_base_url(profile.source_url) return nil if base.empty? show_title = resolve_show_title(profile, fetch: fetch) return nil if show_title.to_s.strip.empty? random = rand.is_a?(Random) ? rand : Random.new SOURCE_TYPES.shuffle(random: random).each do |source_type| MAX_ATTEMPTS.times do page_title = pick_page_title( source_type, profile, show_title: show_title, fetch: fetch, rand: random, store: store ) next if page_title.to_s.strip.empty? round = fetch_round_for_page( base, page_title, source_type: source_type, fetch: fetch, rand: random ) return round if round end end nil rescue StandardError nil end
Source
# File lib/fandom_trivia.rb, line 323 def fetch_round_for_page(base_url, page_title, source_type:, fetch: method(:default_fetch), rand: Random) sections = page_sections(base_url, page_title, fetch: fetch) section = trivia_section(sections) return nil unless section text = fetch_section_text(base_url, page_title, section["index"], fetch: fetch) items = parse_trivia_items(text) return nil if items.empty? fact = items.sample(random: rand) round = build_round_from_fact(fact, page_title: page_title, source_type: source_type) round&.to_h end
Source
# File lib/fandom_trivia.rb, line 337 def fetch_round_from_sources(profiles, fetch: method(:default_fetch), rand: ->(max) { Random.rand(0...max) }, store: RabbotDb) representatives = FandomTriviaSources.representatives_by_wiki(Array(profiles)) return nil if representatives.empty? random = rand.is_a?(Random) ? rand : Random.new representatives.shuffle(random: random).each do |profile| round = fetch_round(profile, fetch: fetch, rand: random, store: store) return round if round end nil end
Source
# File lib/fandom_trivia.rb, line 137 def fetch_section_text(base_url, page_title, section_index, fetch: method(:default_fetch)) url = api_url( base_url, action: "parse", page: page_title, prop: "text", section: section_index.to_s ) body = fetch_json(url, fetch: fetch) body.dig("parse", "text", "*").to_s rescue StandardError "" end
Source
# File lib/fandom_trivia.rb, line 125 def page_sections(base_url, page_title, fetch: method(:default_fetch)) url = api_url(base_url, action: "parse", page: page_title, prop: "sections") body = fetch_json(url, fetch: fetch) Array(body.dig("parse", "sections")) rescue StandardError [] end
Source
# File lib/fandom_trivia.rb, line 221 def parse_trivia_items(text) cleaned = clean_wiki_markup(text) return [] if cleaned.empty? items = [] current = +"" cleaned.split("\n").each do |line| stripped = strip_section_header(line.strip) next if stripped.empty? next if stripped.match?(/\A\u2191/) if stripped.match?(/\A[\*\u2022-]\s*/) items << current.strip unless current.strip.empty? current = stripped.sub(/\A[\*\u2022-]\s*/, "") elsif current.empty? current = stripped elsif stripped.length < 40 current = "#{current} #{stripped}" else items << current.strip unless current.strip.empty? current = stripped end end items << current.strip unless current.strip.empty? items.reject { |item| item.length < 20 } end
Source
# File lib/fandom_trivia.rb, line 309 def pick_page_title(source_type, profile, show_title:, fetch:, rand:, store:) base = wiki_base_url(profile.source_url) case source_type when :character titles = character_page_titles(base, fetch: fetch) titles.sample(random: rand) when :episode titles = episode_page_titles(profile.source_url, fetch: fetch, store: store) titles.sample(random: rand) when :general show_title end end
Source
# File lib/fandom_trivia.rb, line 248 def quoted_strings(text) clean_wiki_markup(text).scan(/"([^"]+)"/).flatten.map(&:strip).reject(&:empty?) end
Source
# File lib/fandom_trivia.rb, line 95 def resolve_show_title(profile, fetch: method(:default_fetch)) from_profile = show_title_from_profile(profile) return from_profile if from_profile && !from_profile.empty? base = wiki_base_url(profile.source_url) return nil if base.empty? title = character_title(profile.source_url) return nil if title.empty? intro = fetch_page_intro(base, title, fetch: fetch) show_title_from_intro(intro) rescue StandardError nil end
Source
# File lib/fandom_trivia.rb, line 66 def show_title_from_intro(text) cleaned = clean_wiki_markup(text.to_s) patterns = [ /\bone of the [\w\s]+ characters of ([^.]+)/i, /\bfrom (?:the (?:animated )?)?(?:series|show) ([^.]+)/i, /\bof ([A-Z][^.]{3,}?)\./ ] patterns.each do |pattern| match = cleaned.match(pattern) return match[1].strip if match end nil end
Source
# File lib/fandom_trivia.rb, line 57 def show_title_from_profile(profile) summary = profile.summary.to_s match = summary.match(/\b(?:from|in|of)\s+(.+?)(?:[,.]|$)/i) title = match&.[](1)&.strip return title unless title.to_s.empty? nil end
Source
# File lib/fandom_trivia.rb, line 217 def strip_section_header(line) line.to_s.sub(/\A(?:notes\/trivia|trivia)\s*(?:\[\s*\])?\s*/i, "").strip end
Source
# File lib/fandom_trivia.rb, line 133 def trivia_section(sections) sections.find { |section| section["line"].to_s.match?(TRIVIA_SECTION_PATTERN) } end
Source
# File lib/fandom_trivia.rb, line 302 def truncate(text, max_chars) text = text.to_s.strip return text if text.length <= max_chars "#{text[0, max_chars].sub(/\s+\S*\z/, "")}..." end
Source
# File lib/fandom_trivia.rb, line 40 def wiki_base_url(source_url) uri = URI.parse(source_url.to_s.strip) "#{uri.scheme}://#{uri.host}" rescue URI::InvalidURIError "" end
Source
# File lib/fandom_trivia.rb, line 47 def wiki_host(source_url) URI.parse(source_url.to_s.strip).host.to_s.downcase rescue URI::InvalidURIError "" end