module UrlPreview
Constants
- INITIAL_PARAGRAPH_LIMIT
- MORE_PARAGRAPH_LIMIT
- NO_MORE_MESSAGE
- NO_SESSION_MESSAGE
- SESSION_MUTEX
- STYLE_BANNER
Public Instance Methods
Source
# File lib/url_preview.rb, line 25 def clear_sessions! SESSION_MUTEX.synchronize { sessions.clear } end
Source
# File lib/url_preview.rb, line 79 def extract_paragraphs(html, limit:, offset: 0) needed = offset + limit AnimalNews.extract_article_paragraphs(html, limit: needed).drop(offset).first(limit) end
Source
# File lib/url_preview.rb, line 84 def fallback_paragraphs(_html, metadata:, offset:, limit:) return [] unless offset.zero? description = metadata[:description].to_s.strip.gsub(/\s+/, " ") return [] if description.empty? [description].first(limit) end
Source
# File lib/url_preview.rb, line 102 def format_card(url:, metadata:, paragraphs:) title = metadata[:title].to_s.strip host = host_label(url, metadata) banner_title = host.empty? ? "Link preview" : host lines = [IrcFormat.report_header(tag: "URL", title: banner_title, style: STYLE_BANNER)] lines << title unless title.empty? paragraphs.each { |paragraph| lines << paragraph.to_s.strip unless paragraph.to_s.strip.empty? } lines << IrcFormat.report_footer(url) lines.join("\n") end
Source
# File lib/url_preview.rb, line 114 def format_more(paragraphs:, url:) body = paragraphs.map(&:to_s).map(&:strip).reject(&:empty?) return NO_MORE_MESSAGE if body.empty? lines = body.dup lines << IrcFormat.report_footer(url) lines.join("\n") end
Source
# File lib/url_preview.rb, line 93 def host_label(url, metadata) site = metadata[:site_name].to_s.strip return site unless site.empty? URI.parse(url.to_s).host.to_s rescue URI::InvalidURIError "" end
Source
# File lib/url_preview.rb, line 60 def meta_content(doc, property) doc.at_css(%(meta[property="#{property}"]))&.[]("content").to_s.strip end
Source
# File lib/url_preview.rb, line 44 def parse_command(args) text = args.to_s.strip return { error: usage_message } if text.empty? return { more: true } if text.match?(/\Amore\b/i) url = UrlLink.extract_first_url(text) url = UrlLink.normalize_url(text) if url.nil? && UrlLink.valid_url?(text) return { error: usage_message } unless url { url: url } end
Source
# File lib/url_preview.rb, line 64 def parse_metadata(html) doc = Nokogiri::HTML(html.to_s) title = meta_content(doc, "og:title") title = doc.at_css("title")&.text.to_s.strip if title.empty? description = meta_content(doc, "og:description") description = doc.at_css('meta[name="description"]')&.[]("content").to_s.strip if description.empty? site_name = meta_content(doc, "og:site_name") { title: CGI.unescapeHTML(title), description: CGI.unescapeHTML(description), site_name: CGI.unescapeHTML(site_name) } end
Source
# File lib/url_preview.rb, line 138 def preview(url, fetch:, channel: nil) result = scrape(url, fetch: fetch, offset: 0, limit: INITIAL_PARAGRAPH_LIMIT) return result[:error] if result[:error] store_session(channel, url: url, next_offset: INITIAL_PARAGRAPH_LIMIT) if channel format_card(url: url, metadata: result[:metadata], paragraphs: result[:paragraphs]) end
Source
# File lib/url_preview.rb, line 146 def preview_more(channel:, fetch:) session = session_for(channel) url = session[:url].to_s.strip return NO_SESSION_MESSAGE if url.empty? offset = session[:next_offset] || INITIAL_PARAGRAPH_LIMIT result = scrape(url, fetch: fetch, offset: offset, limit: MORE_PARAGRAPH_LIMIT) return result[:error] if result[:error] paragraphs = result[:paragraphs] return NO_MORE_MESSAGE if paragraphs.empty? store_session(channel, url: url, next_offset: offset + paragraphs.length) format_more(paragraphs: paragraphs, url: url) end
Source
# File lib/url_preview.rb, line 123 def scrape(url, fetch:, offset: 0, limit: INITIAL_PARAGRAPH_LIMIT) html = fetch.call(url) metadata = parse_metadata(html) paragraphs = extract_paragraphs(html, limit: limit, offset: offset) paragraphs = fallback_paragraphs(html, metadata: metadata, offset: offset, limit: limit) if paragraphs.empty? if metadata[:title].to_s.strip.empty? && paragraphs.empty? return { error: "No page metadata found" } end { metadata: metadata, paragraphs: paragraphs } rescue StandardError { error: "Could not fetch URL" } end
Source
# File lib/url_preview.rb, line 29 def session_for(channel) SESSION_MUTEX.synchronize do (sessions[channel.to_s] || {}).dup end end
Source
# File lib/url_preview.rb, line 35 def store_session(channel, url:, next_offset:) channel = channel.to_s return if channel.empty? SESSION_MUTEX.synchronize do sessions[channel] = { url: url, next_offset: next_offset } end end
Source
# File lib/url_preview.rb, line 56 def usage_message "Usage: !url <url> | !url more" end