2024-02-25 12:15:55 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2019-02-23 11:45:10 -05:00
|
|
|
class Takedown < ApplicationRecord
|
2020-02-14 02:17:42 -05:00
|
|
|
belongs_to_creator optional: true
|
2019-02-27 06:47:23 -05:00
|
|
|
belongs_to :approver, class_name: "User", optional: true
|
2019-02-23 11:45:10 -05:00
|
|
|
before_validation :initialize_fields, on: :create
|
|
|
|
before_validation :normalize_post_ids
|
2023-10-30 11:25:20 -04:00
|
|
|
before_validation :strip_fields
|
2019-09-05 08:59:51 -04:00
|
|
|
validates :email, presence: true
|
|
|
|
validates :reason, presence: true
|
|
|
|
validates :email, format: { with: /\A([\s*A-Z0-9._%+-]+@[\s*A-Z0-9.-]+\.\s*[A-Z\s*]{2,15}\s*)\z/i, on: :create }
|
|
|
|
validates :email, length: { maximum: 250 }
|
|
|
|
validates :reason, length: { maximum: 5_000 }
|
|
|
|
validates :instructions, length: { maximum: 5_000 }
|
|
|
|
validates :notes, length: { maximum: 5_000 }
|
2020-04-02 07:05:59 -04:00
|
|
|
validate :can_create_takedown, on: :create
|
|
|
|
validate :valid_posts_or_instructions, on: :create
|
2019-02-23 11:45:10 -05:00
|
|
|
validate :validate_number_of_posts
|
|
|
|
validate :validate_post_ids
|
|
|
|
after_validation :normalize_deleted_post_ids
|
|
|
|
before_save :update_post_count
|
|
|
|
|
|
|
|
def initialize_fields
|
|
|
|
self.status = "pending"
|
|
|
|
self.vericode = Takedown.create_vericode
|
|
|
|
self.del_post_ids = ''
|
|
|
|
end
|
|
|
|
|
2023-10-30 11:25:20 -04:00
|
|
|
def strip_fields
|
|
|
|
self.email = email&.strip
|
|
|
|
self.source = source&.strip
|
|
|
|
end
|
|
|
|
|
2019-02-23 11:45:10 -05:00
|
|
|
def self.create_vericode
|
|
|
|
consonants = "bcdfghjklmnpqrstvqxyz"
|
|
|
|
vowels = "aeiou"
|
|
|
|
pass = ""
|
|
|
|
|
|
|
|
4.times do
|
2024-02-27 11:32:34 -05:00
|
|
|
pass += consonants[rand(21), 1]
|
|
|
|
pass += vowels[rand(5), 1]
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
2024-02-27 11:32:34 -05:00
|
|
|
pass += rand(100).to_s
|
2019-02-23 11:45:10 -05:00
|
|
|
pass
|
|
|
|
end
|
|
|
|
|
|
|
|
module ValidationMethods
|
|
|
|
def valid_posts_or_instructions
|
2020-02-14 02:17:42 -05:00
|
|
|
if post_array.size <= 0 && instructions.blank?
|
2021-02-07 21:56:22 -05:00
|
|
|
errors.add(:base, "You must provide post ids or instructions.")
|
2020-02-14 02:17:42 -05:00
|
|
|
return false
|
|
|
|
end
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
2019-02-27 06:47:23 -05:00
|
|
|
|
2019-02-23 11:45:10 -05:00
|
|
|
def can_create_takedown
|
2023-02-26 07:26:08 -05:00
|
|
|
return if creator&.is_admin?
|
|
|
|
|
|
|
|
if IpBan.is_banned?(creator_ip_addr.to_s)
|
|
|
|
errors.add(:base, "Something went wrong. Please email us at #{Danbooru.config.takedown_email} instead")
|
|
|
|
return
|
2020-02-14 02:17:42 -05:00
|
|
|
end
|
2023-02-26 07:26:08 -05:00
|
|
|
|
|
|
|
takedowns_ip = Takedown.where(creator_ip_addr: creator_ip_addr, created_at: 1.day.ago..)
|
|
|
|
if takedowns_ip.count > 5
|
|
|
|
errors.add(:base, "You have created too many takedowns. Please email us at #{Danbooru.config.takedown_email} or try again later")
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
takedowns_user = Takedown.where(creator_id: creator_id, created_at: 1.day.ago..)
|
|
|
|
if creator_id && takedowns_user.count > 5
|
|
|
|
errors.add(:base, "You have created too many takedowns. Please email us at #{Danbooru.config.takedown_email} or try again later")
|
2020-02-14 02:17:42 -05:00
|
|
|
end
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
2019-02-27 06:47:23 -05:00
|
|
|
|
2019-02-23 11:45:10 -05:00
|
|
|
def validate_number_of_posts
|
|
|
|
if post_array.size > 5_000
|
|
|
|
self.errors.add(:base, "You can only have 5000 posts in a takedown.")
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-27 06:47:23 -05:00
|
|
|
module AccessMethods
|
|
|
|
def can_edit?(user)
|
|
|
|
user.is_admin?
|
|
|
|
end
|
|
|
|
|
|
|
|
def can_delete?(user)
|
|
|
|
user.is_admin?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module ModifyPostMethods
|
2019-02-23 11:45:10 -05:00
|
|
|
def add_posts_by_ids!(ids)
|
2019-02-27 06:47:23 -05:00
|
|
|
added_ids = []
|
2019-02-23 11:45:10 -05:00
|
|
|
with_lock do
|
2022-10-17 13:38:18 -04:00
|
|
|
self.post_ids = (post_array + matching_post_ids(ids)).uniq.join(' ')
|
2019-02-27 06:47:23 -05:00
|
|
|
added_ids = self.post_array - self.post_array_was
|
2019-02-23 11:45:10 -05:00
|
|
|
save!
|
|
|
|
end
|
2019-02-27 06:47:23 -05:00
|
|
|
added_ids
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def add_posts_by_tags!(tag_string)
|
2023-09-21 13:06:18 -04:00
|
|
|
new_ids = Post.tag_match_system("#{tag_string} -status:deleted").limit(1000).pluck(:id)
|
2023-09-11 12:30:25 -04:00
|
|
|
add_posts_by_ids!(new_ids.join(" "))
|
2019-02-27 06:47:23 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def remove_posts_by_ids!(ids)
|
|
|
|
with_lock do
|
2022-10-17 13:38:18 -04:00
|
|
|
self.post_ids = (post_array - matching_post_ids(ids)).uniq.join(' ')
|
2019-02-27 06:47:23 -05:00
|
|
|
save!
|
|
|
|
end
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
2022-10-17 13:38:18 -04:00
|
|
|
|
|
|
|
def matching_post_ids(input)
|
|
|
|
input.scan(%r{(?:https://(?:e621|e926)\.net/posts/)?(\d+)}i).flatten.map(&:to_i).uniq
|
|
|
|
end
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
module PostMethods
|
2019-02-27 06:47:23 -05:00
|
|
|
def should_delete(id)
|
|
|
|
del_post_array.include?(id)
|
|
|
|
end
|
|
|
|
|
2019-02-23 11:45:10 -05:00
|
|
|
def normalize_post_ids
|
2022-10-17 13:38:18 -04:00
|
|
|
self.post_ids = matching_post_ids(post_ids).join(' ')
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def normalize_deleted_post_ids
|
2022-10-17 13:38:18 -04:00
|
|
|
posts = matching_post_ids(post_ids)
|
|
|
|
del_posts = matching_post_ids(del_post_ids)
|
2019-02-23 11:45:10 -05:00
|
|
|
del_posts = del_posts & posts # ensure that all deleted posts are also posts
|
|
|
|
self.del_post_ids = del_posts.join(' ')
|
|
|
|
end
|
|
|
|
|
|
|
|
def validate_post_ids
|
|
|
|
temp_post_ids = Post.select(:id).where(id: post_array).map {|x| x.id.to_s}
|
|
|
|
self.post_ids = temp_post_ids.join(' ')
|
|
|
|
end
|
|
|
|
|
|
|
|
def del_post_array
|
2022-10-17 13:38:18 -04:00
|
|
|
matching_post_ids(del_post_ids)
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def actual_deleted_posts
|
2019-02-27 06:47:23 -05:00
|
|
|
@actual_deleted_posts ||= Post.where(id: del_post_array)
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def post_array
|
2022-10-17 13:38:18 -04:00
|
|
|
matching_post_ids(post_ids)
|
2019-02-27 06:47:23 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def post_array_was
|
2022-10-17 13:38:18 -04:00
|
|
|
matching_post_ids(post_ids_was)
|
2019-02-27 06:47:23 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def actual_posts
|
|
|
|
@actual_posts ||= Post.where(id: post_array)
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def actual_kept_posts
|
2019-02-27 06:47:23 -05:00
|
|
|
@actual_kept_posts ||= Post.where(id: kept_post_array)
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def kept_post_array
|
|
|
|
@kept_post_array ||= post_array - del_post_array
|
|
|
|
end
|
|
|
|
|
|
|
|
def clear_cached_arrays
|
2019-02-27 06:47:23 -05:00
|
|
|
@actual_posts = @actual_deleted_posts = @actual_kept_posts = nil
|
|
|
|
@post_array = @del_post_array = @kept_post_array = nil
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def update_post_count
|
|
|
|
normalize_post_ids
|
|
|
|
normalize_deleted_post_ids
|
|
|
|
clear_cached_arrays
|
2019-06-05 13:29:00 -04:00
|
|
|
self.post_count = post_array.size
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module ProcessMethods
|
2019-02-27 06:47:23 -05:00
|
|
|
def apply_posts(posts)
|
|
|
|
to_del = []
|
2020-05-05 22:52:51 -04:00
|
|
|
posts ||= []
|
2019-02-27 06:47:23 -05:00
|
|
|
posts.each do |post_id, keep|
|
|
|
|
if keep == '1'
|
|
|
|
to_del << post_id
|
|
|
|
end
|
|
|
|
end
|
|
|
|
to_del.map!(&:to_i)
|
|
|
|
self.del_post_ids = to_del
|
|
|
|
end
|
2019-02-23 11:45:10 -05:00
|
|
|
|
2019-02-27 06:47:23 -05:00
|
|
|
def process!(approver, del_reason)
|
2023-05-01 12:19:26 -04:00
|
|
|
TakedownJob.perform_later(id, approver.id, del_reason)
|
2019-02-27 06:47:23 -05:00
|
|
|
end
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
module SearchMethods
|
|
|
|
def search(params)
|
|
|
|
q = super
|
|
|
|
|
|
|
|
if params[:source].present?
|
2020-03-07 18:11:29 -05:00
|
|
|
q = q.where_ilike(:source, params[:source])
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
if params[:reason].present?
|
2020-03-07 18:11:29 -05:00
|
|
|
q = q.where_ilike(:reason, params[:reason])
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
2020-04-16 20:33:06 -04:00
|
|
|
if params[:post_id].present?
|
|
|
|
post_id = params[:post_id].to_i
|
|
|
|
q = q.where('post_ids ~ ?', "(^| )#{post_id}($| )")
|
|
|
|
end
|
|
|
|
if params[:instructions].present?
|
|
|
|
q = q.where_ilike(:instructions, params[:instructions])
|
|
|
|
end
|
|
|
|
if params[:notes].present?
|
|
|
|
q = q.where_ilike(:notes, params[:notes])
|
|
|
|
end
|
|
|
|
if params[:reason_hidden].present?
|
|
|
|
q = q.where('reason_hidden = ?', params[:reason_hidden])
|
|
|
|
end
|
2019-02-23 11:45:10 -05:00
|
|
|
if params[:ip_addr].present?
|
|
|
|
q = q.where('creator_ip_addr <<= ?', params[:ip_addr])
|
|
|
|
end
|
2023-08-03 16:01:53 -04:00
|
|
|
q = q.where_user(:creator_id, :creator, params)
|
2019-02-23 11:45:10 -05:00
|
|
|
if params[:email].present?
|
2020-03-07 18:11:29 -05:00
|
|
|
q = q.where_ilike(:email, params[:email])
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
if params[:vericode].present?
|
|
|
|
q = q.where('vericode = ?', params[:vericode])
|
|
|
|
end
|
|
|
|
if params[:status].present?
|
|
|
|
q = q.where('status = ?', params[:status])
|
|
|
|
end
|
|
|
|
|
|
|
|
case params[:order]
|
|
|
|
when 'status'
|
|
|
|
q = q.order('status ASC')
|
|
|
|
when 'post_count'
|
|
|
|
q = q.order('post_count DESC')
|
|
|
|
else
|
2023-07-07 08:32:57 -04:00
|
|
|
q = q.apply_basic_order(params)
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
q
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module StatusMethods
|
|
|
|
def completed?
|
|
|
|
["approved", "denied", "partial"].include?(status)
|
|
|
|
end
|
|
|
|
|
|
|
|
def calculated_status
|
2019-02-27 06:47:23 -05:00
|
|
|
kept_count = kept_post_array.size
|
|
|
|
deleted_count = del_post_array.size
|
2019-02-23 11:45:10 -05:00
|
|
|
|
|
|
|
if kept_count == 0 # All were deleted, so it was approved
|
|
|
|
"approved"
|
|
|
|
elsif deleted_count == 0 # All were kept, so it was denied
|
|
|
|
"denied"
|
|
|
|
else # Some were kept and some were deleted, so it was partially approved
|
|
|
|
"partial"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-05-05 13:01:44 -04:00
|
|
|
module APIMethods
|
|
|
|
def hidden_attributes
|
|
|
|
super + [:source, :vericode, :email, :reason, :instructions, :post_ids, :del_post_ids, :creator_id, :notes]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-23 11:45:10 -05:00
|
|
|
include PostMethods
|
2020-05-05 13:01:44 -04:00
|
|
|
include APIMethods
|
2019-02-23 11:45:10 -05:00
|
|
|
include ValidationMethods
|
|
|
|
include StatusMethods
|
2019-02-27 06:47:23 -05:00
|
|
|
include ModifyPostMethods
|
2019-02-23 11:45:10 -05:00
|
|
|
include ProcessMethods
|
2019-03-13 17:04:59 -04:00
|
|
|
extend SearchMethods
|
2019-02-27 06:47:23 -05:00
|
|
|
include AccessMethods
|
2019-02-23 11:45:10 -05:00
|
|
|
end
|