richardvenneman/instagrammer

View on GitHub
lib/instagrammer/user.rb

Summary

Maintainability
A
40 mins
Test Coverage
# frozen_string_literal: true

class Instagrammer::User
  include Capybara::DSL
  include Instagrammer::Utils

  attr_reader :posts

  def initialize(username)
    @username = username.delete_prefix("@")
    @data = nil
    @posts = []
  end

  def public?
    get_data unless @data
    @status == :public
  end

  def meta
    get_data unless @data

    if @status == :not_found
      raise Instagrammer::UserNotFound.new("Private account: #{@username}")
    else
      @meta
    end
  end

  def follower_count
    meta[:followers]
  end

  def following_count
    meta[:following]
  end

  def post_count
    meta[:posts]
  end

  def data
    get_data unless @data

    case @status
    when :private then raise Instagrammer::PrivateAccount.new("Private account: #{@username}")
    when :not_found then raise Instagrammer::UserNotFound.new("User not found: #{@username}")
    when :invalid then raise Instagrammer::UserInvalid.new("User invalid: #{@username}")
    else @data
    end
  end

  def name
    data["name"]
  end

  def username
    data["alternateName"].delete_prefix("@")
  end

  def avatar
    data["image"]
  end

  def bio
    data["description"]
  end

  def url
    data["mainEntityofPage"]["@id"]
  end

  SHORTCODE_RE = /\/p\/(\S+)\/$/
  def get_posts(limit)
    shortcodes = []
    i = 0

    visit "https://www.instagram.com/#{@username}/"
    while i < limit
      post_links = page.all(:post_link)

      if limit > post_links.size
        page.execute_script "window.scrollTo(0,document.body.scrollHeight);"
        post_links = page.all(:post_link)
      end

      shortcode = post_links[i]["href"].match(SHORTCODE_RE)[1]
      shortcodes << shortcode
      i += 1
    end

    @posts = shortcodes.collect { |code| Instagrammer::Post.new(code) }
  end

  private
    def get_data
      visit "https://www.instagram.com/#{@username}/"
      @status = get_page_status
      @meta = get_metadata unless @status == :not_found

      if @status == :public
        node = page.first(:json_ld, visible: false)
        @data = JSON.parse(node.text(:all))
      end
    end

    META_RE = /(?<followers>\S+) Followers, (?<following>\S+) Following, (?<posts>\S+) Posts/
    def get_metadata
      @meta = page.first(:meta_description, visible: false)["content"].match META_RE
    end
end