forked from e621ng/e621ng
Add user based action throttles
This commit is contained in:
parent
18f0cdf334
commit
c819a34443
@ -7,6 +7,7 @@ class Artist < ApplicationRecord
|
||||
|
||||
before_validation :normalize_name
|
||||
before_validation :normalize_other_names
|
||||
validate :user_not_limited
|
||||
after_save :create_version
|
||||
after_save :categorize_tag
|
||||
after_save :update_wiki
|
||||
|
@ -18,9 +18,12 @@ class Blip < ApplicationRecord
|
||||
end
|
||||
|
||||
def validate_creator_is_not_limited
|
||||
unless creator.can_comment?
|
||||
errors.add(:base, "You may not create blips until your account is three days old")
|
||||
allowed = creator.can_blip_with_reason
|
||||
if allowed != true
|
||||
errors.add(:creator, User.throttle_reason(allowed))
|
||||
false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def validate_parent_exists
|
||||
|
@ -112,15 +112,12 @@ class Comment < ApplicationRecord
|
||||
end
|
||||
|
||||
def validate_creator_is_not_limited
|
||||
if creator.is_comment_limited? && !do_not_bump_post?
|
||||
errors.add(:base, "You can only post #{Danbooru.config.member_comment_limit} comments per hour")
|
||||
false
|
||||
elsif creator.can_comment?
|
||||
true
|
||||
else
|
||||
errors.add(:base, "You can not post comments within 1 week of sign up")
|
||||
allowed = creator.can_comment_with_reason
|
||||
if allowed != true
|
||||
errors.add(:creator, User.throttle_reason(allowed))
|
||||
false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def update_last_commented_at_on_create
|
||||
|
@ -10,6 +10,7 @@ class Dmail < ApplicationRecord
|
||||
|
||||
validates_presence_of :title, :body, on: :create
|
||||
validate :validate_sender_is_not_banned, on: :create
|
||||
validate :user_not_limited, on: :create
|
||||
|
||||
belongs_to :owner, :class_name => "User"
|
||||
belongs_to :to, :class_name => "User"
|
||||
@ -211,6 +212,16 @@ class Dmail < ApplicationRecord
|
||||
include ApiMethods
|
||||
extend SearchMethods
|
||||
|
||||
def user_not_limited
|
||||
allowed = CurrentUser.can_dmail_with_reason
|
||||
minute_allowed = CurrentUser.can_dmail_minute_with_reason
|
||||
if allowed != true || minute_allowed != true
|
||||
errors.add(:base, "Sender #{User.throttle_reason(allowed != true ? allowed : minute_allowed)}.")
|
||||
false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def validate_sender_is_not_banned
|
||||
if from.try(:is_banned?)
|
||||
errors[:base] << "Sender is banned and cannot send messages"
|
||||
|
@ -6,6 +6,7 @@ class Note < ApplicationRecord
|
||||
belongs_to_creator
|
||||
has_many :versions, -> {order("note_versions.id ASC")}, :class_name => "NoteVersion", :dependent => :destroy
|
||||
validates_presence_of :post_id, :creator_id, :x, :y, :width, :height, :body
|
||||
validate :user_not_limited
|
||||
validate :post_must_exist
|
||||
validate :note_within_image
|
||||
after_save :update_post
|
||||
@ -68,6 +69,15 @@ class Note < ApplicationRecord
|
||||
extend SearchMethods
|
||||
include ApiMethods
|
||||
|
||||
def user_not_limited
|
||||
allowed = CurrentUser.can_not_edit_with_reason
|
||||
if allowed != true
|
||||
errors.add(:base, "User #{User.throttle_reason(allowed)}.")
|
||||
false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def post_must_exist
|
||||
if !Post.exists?(post_id)
|
||||
errors.add :post, "must exist"
|
||||
|
@ -6,6 +6,8 @@ class Pool < ApplicationRecord
|
||||
belongs_to_creator
|
||||
|
||||
validates_uniqueness_of :name, case_sensitive: false, if: :name_changed?
|
||||
validate :user_not_create_limited, on: :create
|
||||
validate :user_not_limited, on: :update
|
||||
validate :validate_name, if: :name_changed?
|
||||
validates_inclusion_of :category, :in => %w(series collection)
|
||||
validate :updater_can_change_category
|
||||
@ -19,6 +21,10 @@ class Pool < ApplicationRecord
|
||||
after_create :synchronize!
|
||||
|
||||
module SearchMethods
|
||||
def for_user(id)
|
||||
where("pools.creator_id = ?", id)
|
||||
end
|
||||
|
||||
def deleted
|
||||
where("pools.is_deleted = true")
|
||||
end
|
||||
@ -99,6 +105,24 @@ class Pool < ApplicationRecord
|
||||
|
||||
extend SearchMethods
|
||||
|
||||
def user_not_create_limited
|
||||
allowed = creator.can_pool_with_reason
|
||||
if allowed != true
|
||||
errors.add(:creator, User.throttle_reason(allowed))
|
||||
false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def user_not_limited
|
||||
allowed = CurrentUser.can_pool_edit_with_reason
|
||||
if allowed != true
|
||||
errors.add(:updater, User.throttle_reason(allowed))
|
||||
false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def self.name_to_id(name)
|
||||
if name =~ /^\d+$/
|
||||
name.to_i
|
||||
|
@ -426,13 +426,69 @@ class User < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
module ThrottleMethods
|
||||
def throttle_reason(reason)
|
||||
reasons = {
|
||||
REJ_NEWBIE: 'can not yet perform this action. User not old enough',
|
||||
REJ_LIMITED: 'has reached the hourly limit for this action'
|
||||
}
|
||||
reasons.fetch(reason, 'unknown throttle reason, please report this as a bug')
|
||||
end
|
||||
|
||||
def upload_reason_string(reason)
|
||||
reasons = {
|
||||
REJ_UPLOAD_HOURLY: "have reached your hourly upload limit",
|
||||
REJ_UPLOAD_EDIT: "have no remaining tag edits available",
|
||||
REJ_UPLOAD_LIMIT: "have reached your upload limit",
|
||||
REJ_UPLOAD_NEWBIE: "cannot upload during your first week"
|
||||
}
|
||||
reasons.fetch(reason, "unknown upload rejection reason")
|
||||
end
|
||||
end
|
||||
|
||||
module LimitMethods
|
||||
extend Memoist
|
||||
|
||||
def self.create_user_throttle(name, limiter, checker, newbie_duration)
|
||||
define_method("#{name}_limit".to_sym, limiter)
|
||||
memoize "#{name}_limit".to_sym
|
||||
define_method("can_#{name}_with_reason".to_sym) do
|
||||
return :REJ_NEWBIE if newbie_duration && created_at > newbie_duration
|
||||
return send(checker) if checker && send(checker)
|
||||
return :REJ_LIMITED if send("#{name}_limit") <= 0
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def token_bucket
|
||||
@token_bucket ||= UserThrottle.new({prefix: "thtl:", duration: 1.minute}, self)
|
||||
end
|
||||
|
||||
def general_should_throttle?
|
||||
!is_platinum?
|
||||
end
|
||||
|
||||
create_user_throttle(:artist_edit, ->{ Danbooru.config.artist_edit_limit - ArtistVersion.for_user(id).where('updated_at > ?', 1.hour.ago).count },
|
||||
:general_should_throttle?, 7.days.ago)
|
||||
create_user_throttle(:post_edit, ->{ Danbooru.config.post_edit_limit - PostArchive.for_user(id).where('updated_at > ?', 1.hour.ago).count },
|
||||
:general_should_throttle?, 3.days.ago)
|
||||
create_user_throttle(:wiki_edit, ->{ Danbooru.config.wiki_edit_limit - WikiPageVersion.for_user(id).where('updated_at > ?', 1.hour.ago).count },
|
||||
:general_should_throttle?, 7.days.ago)
|
||||
create_user_throttle(:pool, ->{ Danbooru.config.pool_limit - Pool.for_user(id).where('created_at > ?', 1.hour.ago).count },
|
||||
nil, 7.days.ago)
|
||||
create_user_throttle(:pool_edit, ->{ Danbooru.config.pool_edit_limit - PoolArchive.for_user(id).where('updated_at > ?', 1.hour.ago).count },
|
||||
nil, 7.days.ago)
|
||||
create_user_throttle(:note_edit, ->{ Danbooru.config.note_edit_limit - NoteVersion.for_user(id).where('updated_at > ?', 1.hour.ago).count },
|
||||
:general_should_throttle?, 7.days.ago)
|
||||
create_user_throttle(:comment, ->{ Danbooru.config.member_comment_limit - Comment.for_creator(id).where('created_at > ?', 1.hour.ago).count },
|
||||
:general_should_throttle?, 7.days.ago)
|
||||
create_user_throttle(:blip, ->{ Danbooru.config.blip_limit - Blip.for_creator(id).where('created_at > ?', 1.hour.ago).count },
|
||||
:general_should_throttle?, 3.days.ago)
|
||||
create_user_throttle(:dmail, ->{ Danbooru.config.dmail_limit - Dmail.sent_by(id).where('created_at > ?', 1.hour.ago).count },
|
||||
nil, nil)
|
||||
create_user_throttle(:dmail_minute, ->{ Danbooru.config.dmail_minute_limit - Dmail.sent_by(id).where('created_at > ?', 1.minute.ago).count },
|
||||
nil, nil)
|
||||
|
||||
def max_saved_searches
|
||||
if is_platinum?
|
||||
1_000
|
||||
@ -445,34 +501,6 @@ class User < ApplicationRecord
|
||||
true
|
||||
end
|
||||
|
||||
def can_edit_with_reason
|
||||
return :REJ_EDIT_NEWBIE if created_at > 3.days.ago
|
||||
return true if is_platinum?
|
||||
return :REJ_EDIT_LIMIT if post_edit_limit <= 0
|
||||
true
|
||||
end
|
||||
|
||||
def post_edit_limit
|
||||
Danbooru.config.post_edit_limit - PostArchive.for_user(id).where('updated_at > ?', 1.hour.ago).count
|
||||
end
|
||||
memoize :post_edit_limit
|
||||
|
||||
def can_comment?
|
||||
if is_gold?
|
||||
true
|
||||
else
|
||||
created_at <= Danbooru.config.member_comment_time_threshold
|
||||
end
|
||||
end
|
||||
|
||||
def is_comment_limited?
|
||||
if is_gold?
|
||||
false
|
||||
else
|
||||
Comment.where("creator_id = ? and created_at > ?", id, 1.hour.ago).count >= Danbooru.config.member_comment_limit
|
||||
end
|
||||
end
|
||||
|
||||
def can_comment_vote?
|
||||
CommentVote.where("user_id = ? and created_at > ?", id, 1.hour.ago).count < 10
|
||||
end
|
||||
@ -511,16 +539,6 @@ class User < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def self.upload_reason_string(reason)
|
||||
reasons = {
|
||||
REJ_UPLOAD_HOURLY: "have reached your hourly upload limit",
|
||||
REJ_UPLOAD_EDIT: "have no remaining tag edits available",
|
||||
REJ_UPLOAD_LIMIT: "have reached your upload limit",
|
||||
REJ_UPLOAD_NEWBIE: "cannot upload during your first week"
|
||||
}
|
||||
reasons.fetch(reason, "unknown upload rejection reason")
|
||||
end
|
||||
|
||||
def upload_limited_reason
|
||||
User.upload_reason_string(can_upload_with_reason)
|
||||
end
|
||||
@ -874,6 +892,7 @@ class User < ApplicationRecord
|
||||
include ApiMethods
|
||||
include CountMethods
|
||||
extend SearchMethods
|
||||
extend ThrottleMethods
|
||||
include StatisticsMethods
|
||||
|
||||
def as_current(&block)
|
||||
|
@ -7,6 +7,7 @@ class WikiPage < ApplicationRecord
|
||||
validates_uniqueness_of :title, :case_sensitive => false
|
||||
validates_presence_of :title
|
||||
validates_presence_of :body, :unless => -> { is_deleted? || other_names.present? }
|
||||
validate :user_not_limited, on: :save
|
||||
validate :validate_rename
|
||||
validate :validate_not_locked
|
||||
|
||||
@ -125,6 +126,15 @@ class WikiPage < ApplicationRecord
|
||||
extend SearchMethods
|
||||
include ApiMethods
|
||||
|
||||
def user_not_limited
|
||||
allowed = CurrentUser.can_wiki_edit_with_reason
|
||||
if allowed != true
|
||||
errors.add(:base, "User #{User.throttle_reason(allowed)}.")
|
||||
false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def validate_not_locked
|
||||
if is_locked? && !CurrentUser.is_builder?
|
||||
errors.add(:is_locked, "and cannot be updated")
|
||||
|
@ -167,9 +167,47 @@ module Danbooru
|
||||
|
||||
# Members cannot post more than X comments in an hour.
|
||||
def member_comment_limit
|
||||
15
|
||||
end
|
||||
|
||||
def dmail_limit
|
||||
20
|
||||
end
|
||||
|
||||
def dmail_minute_limit
|
||||
1
|
||||
end
|
||||
|
||||
# Blips created in the last hour
|
||||
def blip_limit
|
||||
25
|
||||
end
|
||||
|
||||
# Artists creator or edited in the last hour
|
||||
def artist_edit_limit
|
||||
25
|
||||
end
|
||||
|
||||
# Wiki pages created or edited in the last hour
|
||||
def wiki_edit_limit
|
||||
60
|
||||
end
|
||||
|
||||
# Notes applied to posts edited or created in the last hour
|
||||
def note_edit_limit
|
||||
50
|
||||
end
|
||||
|
||||
# Pools created in the last hour
|
||||
def pool_limit
|
||||
2
|
||||
end
|
||||
|
||||
# Pools created or edited in the last hour
|
||||
def pool_edit_limit
|
||||
10
|
||||
end
|
||||
|
||||
# Members cannot create more than X post versions in an hour.
|
||||
def post_edit_limit
|
||||
150
|
||||
|
Loading…
Reference in New Issue
Block a user