<commit_before>class User < ApplicationRecord
  include PgSearch::Model
  include AuthenticatingUser
  include RivalUser
  include RunnerUser

  has_many :runs, dependent: :nullify
  has_many :categories, -> { distinct }, through: :runs
  has_many :games,      -> { distinct }, through: :runs

  has_many :run_likes, dependent: :destroy

  has_many :entries, foreign_key: 'runner_id', dependent: :nullify
  has_many :created_entries, foreign_key: 'creator_id'
  has_many :races, -> { joins(:entries).where(entries: {ghost: false}) }, through: :entries
  has_many :created_races, class_name: 'Race', dependent: :nullify
  has_many :chat_messages, dependent: :nullify

  has_many :rivalries,          foreign_key: :from_user_id, dependent: :destroy, inverse_of: 'from_user'
  has_many :incoming_rivalries, foreign_key: :to_user_id,   dependent: :destroy, inverse_of: 'to_user',
                                class_name: 'Rivalry'

  has_many :twitch_user_follows,   foreign_key: :from_user_id, dependent: :destroy, inverse_of: 'from_user'
  has_many :twitch_user_followers, foreign_key: :to_user_id,   dependent: :destroy, inverse_of: 'to_user',
                                   class_name: 'TwitchUserFollow'

  has_many :twitch_follows,   through: :twitch_user_follows,   source: :to_user
  has_many :twitch_followers, through: :twitch_user_followers, source: :from_user

  has_one :patreon, dependent: :destroy, class_name: 'PatreonUser'
  has_one :twitch,  dependent: :destroy, class_name: 'TwitchUser'
  has_one :google,  dependent: :destroy, class_name: 'GoogleUser'
  has_one :srdc,    dependent: :destroy, class_name: 'SpeedrunDotComUser'

  has_many :subscriptions, dependent: :destroy

  has_many :applications,  class_name: 'Doorkeeper::Application', dependent: :destroy, foreign_key: :owner_id,
                           inverse_of: 'owner'
  has_many :access_grants, class_name: 'Doorkeeper::AccessGrant', dependent: :destroy, foreign_key: :resource_owner_id
  has_many :access_tokens, class_name: 'Doorkeeper::AccessToken', dependent: :destroy, foreign_key: :resource_owner_id

  has_many :sessions, class_name: 'Authie::Session', as: :user, dependent: :destroy

  NAME_REGEX     = /\A[A-Za-z0-9_]+\z/.freeze
  PASSWORD_REGEX = /\A.{8,}+\z/.freeze

  # Turn off default password validations because they include a presence
  # validation, which we don't require as some accounts can be link-only. But
  # turn on the other validations manually -- validating that `password` and
  # `password_confirmation` match when both supplied, and max length.
  #
  # Although it's theoretically bad to have max lengths on passwords, BCrypt
  # truncates at 72 bytes, so we should enforce that here rather than let users
  # pass in 73 bytes and be surprised that we only use the first 72.
  has_secure_password(validations: false)
  validates_confirmation_of :password, allow_blank: true
  validates_length_of       :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED

  validates :name, presence: true, uniqueness: true, format: {with: NAME_REGEX}
  validates :email, presence: true, uniqueness: true # TODO: Add a not-null to database

  scope :with_runs, -> { joins(:runs).distinct }
  scope :that_run, ->(category) { joins(:runs).where(runs: {category: category}).distinct }
  pg_search_scope :search_for_name, against: :name, using: :trigram

  # STRIPE_MIGRATION_DATE decides what the cutoff date for being grandfathered into features due to a Patreon
  # subscription is.
  STRIPE_MIGRATION_DATE = Time.new('2019', '12', '01').utc

  def self.search(term)
    term = term.strip
    return nil if term.blank?

    search_for_name(term)
  end

  # stay_insideathon is meant to help people deal with COVID-19, conditions where they might find themselves staying
  # indoors a lot. If stay_insideathon returns true, all paid features become available to non-subscribers.
  def self.stay_insideathon?
    false
  end

  def avatar<commit_msg>Fix sign-in for some users<commit_after>class User < ApplicationRecord
  include PgSearch::Model
  include AuthenticatingUser
  include RivalUser
  include RunnerUser

  has_many :runs, dependent: :nullify
  has_many :categories, -> { distinct }, through: :runs
  has_many :games,      -> { distinct }, through: :runs

  has_many :run_likes, dependent: :destroy

  has_many :entries, foreign_key: 'runner_id', dependent: :nullify
  has_many :created_entries, foreign_key: 'creator_id'
  has_many :races, -> { joins(:entries).where(entries: {ghost: false}) }, through: :entries
  has_many :created_races, class_name: 'Race', dependent: :nullify
  has_many :chat_messages, dependent: :nullify

  has_many :rivalries,          foreign_key: :from_user_id, dependent: :destroy, inverse_of: 'from_user'
  has_many :incoming_rivalries, foreign_key: :to_user_id,   dependent: :destroy, inverse_of: 'to_user',
                                class_name: 'Rivalry'

  has_many :twitch_user_follows,   foreign_key: :from_user_id, dependent: :destroy, inverse_of: 'from_user'
  has_many :twitch_user_followers, foreign_key: :to_user_id,   dependent: :destroy, inverse_of: 'to_user',
                                   class_name: 'TwitchUserFollow'

  has_many :twitch_follows,   through: :twitch_user_follows,   source: :to_user
  has_many :twitch_followers, through: :twitch_user_followers, source: :from_user

  has_one :patreon, dependent: :destroy, class_name: 'PatreonUser'
  has_one :twitch,  dependent: :destroy, class_name: 'TwitchUser'
  has_one :google,  dependent: :destroy, class_name: 'GoogleUser'
  has_one :srdc,    dependent: :destroy, class_name: 'SpeedrunDotComUser'

  has_many :subscriptions, dependent: :destroy

  has_many :applications,  class_name: 'Doorkeeper::Application', dependent: :destroy, foreign_key: :owner_id,
                           inverse_of: 'owner'
  has_many :access_grants, class_name: 'Doorkeeper::AccessGrant', dependent: :destroy, foreign_key: :resource_owner_id
  has_many :access_tokens, class_name: 'Doorkeeper::AccessToken', dependent: :destroy, foreign_key: :resource_owner_id

  has_many :sessions, class_name: 'Authie::Session', as: :user, dependent: :destroy

  NAME_REGEX     = /\A[A-Za-z0-9_]+\z/.freeze
  PASSWORD_REGEX = /\A.{8,}+\z/.freeze

  # Turn off default password validations because they include a presence
  # validation, which we don't require as some accounts can be link-only. But
  # turn on the other validations manually -- validating that `password` and
  # `password_confirmation` match when both supplied, and max length.
  #
  # Although it's theoretically bad to have max lengths on passwords, BCrypt
  # truncates at 72 bytes, so we should enforce that here rather than let users
  # pass in 73 bytes and be surprised that we only use the first 72.
  has_secure_password(validations: false)
  validates_confirmation_of :password, allow_blank: true
  validates_length_of       :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED

  validates :name, presence: true, uniqueness: true, format: {with: NAME_REGEX}
  validates :email, uniqueness: {allow_nil: true} # Cannot validate non-null as some old users do not have emails

  scope :with_runs, -> { joins(:runs).distinct }
  scope :that_run, ->(category) { joins(:runs).where(runs: {category: category}).distinct }
  pg_search_scope :search_for_name, against: :name, using: :trigram

  # STRIPE_MIGRATION_DATE decides what the cutoff date for being grandfathered into features due to a Patreon
  # subscription is.
  STRIPE_MIGRATION_DATE = Time.new('2019', '12', '01').utc

  def self.search(term)
    term = term.strip
    return nil if term.blank?

    search_for_name(term)
  end

  # stay_insideathon is meant to help people deal with COVID-19, conditions where they might find themselves staying
  # indoors a lot. If stay_insideathon returns true, all paid features become available to non-subscribers.
  def self.stay_insideathon?
    false
  end

  def avatar
{'fraction_non_alphanumeric': 0.0960070096382526, 'fraction_numerical': 0.004756540242833896, 'mean_word_length': 4.540915395284327, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2178810', 'n_tokens_mistral': 2618, 'n_tokens_neox': 2486, 'n_words': 732}