view app/models/tweet.rb @ 157:6e6051cd9cca

Or maybe not
author nanaya <me@nanaya.pro>
date Sat, 28 Jul 2018 03:42:23 +0900
parents 1abfa910d83e
children 74422bae017d
line wrap: on
line source

class Tweet
  TIMELINE_OPTIONS = {
    :count => 100,
    :exclude_replies => false,
    :include_rts => true,
    :tweet_mode => :extended,
  }

  def initialize(twitter_id)
    @clients = {}
    @twitter_id = twitter_id
  end

  def cache_expires_time
    (15 + rand(15)).minutes
  end

  def cache_key
    "timeline:v2:#{@twitter_id}/#{Base64.urlsafe_encode64 @twitter_id.to_s}"
  end

  def timeline
    if @timeline.nil?
      raw = Rails.cache.fetch(cache_key, :expires_in => cache_expires_time) do
        client_try(:user_timeline, @twitter_id, TIMELINE_OPTIONS).tap do |data|
          if data[:result] == :ok
            if data[:data].any? && data[:data].first.user.id != @twitter_id
              wrong_user = data[:data].first.user
              Rails.logger.warn "Wrong timeline data. Requested: #{@twitter_id}, got: #{wrong_user.id} (#{wrong_user.name.printable})"
            end

            data[:data] = data[:data].select do |tweet|
              tweet.retweeted_status.nil? || tweet.user.id != tweet.retweeted_status.user.id
            end.map { |tweet| tweet.to_h }
          end
        end
      end

      raise Twitter::Error::NotFound if raw[:result] == :not_found

      @timeline = raw[:data].map { |tweet_hash| Twitter::Tweet.new(tweet_hash) }
    end

    @timeline
  end

  def user
    if @user.nil?
      return timeline.first.user if timeline.any?

      raw = Rails.cache.fetch("user:v1:#{@twitter_id}", :expires_in => cache_expires_time) do
        client_try :user, @twitter_id
      end

      raise Twitter::Error::NotFound if raw[:result] == :not_found

      @user = raw[:data]
    end

    @user
  end

  def client
    @clients[client_config_id] ||=
      Twitter::REST::Client.new do |config|
        $cfg[:twitter][client_config_id].each do |cfg_key, cfg_value|
          config.public_send(:"#{cfg_key}=", cfg_value)
        end
      end
  end

  def client_try(method, *args)
    initial_config_id = client_config_id

    begin
      data = client.public_send method, *args
    rescue Twitter::Error::TooManyRequests
      @client_config_id += 1

      if initial_config_id == client_config_id
        raise
      else
        retry
      end
    rescue Twitter::Error::NotFound
      return { :result => :not_found }
    end

    { :result => :ok, :data => data }
  end

  def client_config_id
    @client_config_id ||= 0

    @client_config_id %= $cfg[:twitter].size
  end
end