Set the sender name and IP addresses explicitly in the controller rather than implicitly in the model. Fixes cases where automated dmails from DanbooruBot had their IP addresses set to the person who triggered the dmail, even though they didn't actually send the dmail themselves.
198 lines
4.5 KiB
Ruby
198 lines
4.5 KiB
Ruby
require 'digest/sha1'
|
|
|
|
class Dmail < ApplicationRecord
|
|
|
|
validates_presence_of :title, :body, on: :create
|
|
validate :validate_sender_is_not_banned, on: :create
|
|
|
|
belongs_to :owner, :class_name => "User"
|
|
belongs_to :to, :class_name => "User"
|
|
belongs_to :from, :class_name => "User"
|
|
has_many :moderation_reports, as: :model
|
|
|
|
before_create :autoreport_spam
|
|
after_save :update_unread_dmail_count
|
|
after_commit :send_email, on: :create
|
|
|
|
api_attributes including: [:key]
|
|
|
|
scope :active, -> { where(is_deleted: false) }
|
|
scope :deleted, -> { where(is_deleted: true) }
|
|
scope :read, -> { where(is_read: true) }
|
|
scope :unread, -> { where(is_read: false) }
|
|
scope :sent, -> { where("dmails.owner_id = dmails.from_id") }
|
|
scope :received, -> { where("dmails.owner_id = dmails.to_id") }
|
|
|
|
module AddressMethods
|
|
def to_name=(name)
|
|
self.to = User.find_by_name(name)
|
|
end
|
|
end
|
|
|
|
module FactoryMethods
|
|
extend ActiveSupport::Concern
|
|
|
|
module ClassMethods
|
|
def create_split(params)
|
|
copy = nil
|
|
|
|
Dmail.transaction do
|
|
# recipient's copy
|
|
copy = Dmail.new(params)
|
|
copy.owner_id = copy.to_id
|
|
copy.save unless copy.to_id == copy.from_id
|
|
|
|
# sender's copy
|
|
copy = Dmail.new(params)
|
|
copy.owner_id = copy.from_id
|
|
copy.is_read = true
|
|
copy.save
|
|
end
|
|
|
|
copy
|
|
end
|
|
|
|
def create_automated(params)
|
|
dmail = Dmail.new(from: User.system, creator_ip_addr: "127.0.0.1", **params)
|
|
dmail.owner = dmail.to
|
|
dmail.save
|
|
dmail
|
|
end
|
|
end
|
|
|
|
def build_response(options = {})
|
|
Dmail.new do |dmail|
|
|
if title =~ /Re:/
|
|
dmail.title = title
|
|
else
|
|
dmail.title = "Re: #{title}"
|
|
end
|
|
dmail.owner_id = from_id
|
|
dmail.body = quoted_body
|
|
dmail.to_id = from_id unless options[:forward]
|
|
dmail.from_id = to_id
|
|
end
|
|
end
|
|
end
|
|
|
|
module SearchMethods
|
|
def visible(user)
|
|
where(owner: user)
|
|
end
|
|
|
|
def sent_by(user)
|
|
where("dmails.from_id = ? AND dmails.owner_id != ?", user.id, user.id)
|
|
end
|
|
|
|
def folder_matches(folder)
|
|
case folder
|
|
when "received"
|
|
active.received
|
|
when "unread"
|
|
active.received.unread
|
|
when "sent"
|
|
active.sent
|
|
when "deleted"
|
|
deleted
|
|
else
|
|
all
|
|
end
|
|
end
|
|
|
|
def search(params)
|
|
q = super
|
|
|
|
q = q.search_attributes(params, :to, :from, :is_read, :is_deleted, :title, :body)
|
|
q = q.text_attribute_matches(:title, params[:title_matches])
|
|
q = q.text_attribute_matches(:body, params[:message_matches], index_column: :message_index)
|
|
|
|
q = q.folder_matches(params[:folder])
|
|
|
|
q.apply_default_order(params)
|
|
end
|
|
end
|
|
|
|
concerning :AuthorizationMethods do
|
|
def verifier
|
|
@verifier ||= Danbooru::MessageVerifier.new(:dmail_link)
|
|
end
|
|
|
|
def key
|
|
verifier.generate(id)
|
|
end
|
|
|
|
def valid_key?(key)
|
|
decoded_id = verifier.verified(key)
|
|
id == decoded_id
|
|
end
|
|
|
|
def visible_to?(user, key)
|
|
owner_id == user.id || valid_key?(key)
|
|
end
|
|
end
|
|
|
|
include AddressMethods
|
|
include FactoryMethods
|
|
extend SearchMethods
|
|
|
|
def self.mark_all_as_read
|
|
unread.update(is_read: true)
|
|
end
|
|
|
|
def validate_sender_is_not_banned
|
|
if from.try(:is_banned?)
|
|
errors[:base] << "Sender is banned and cannot send messages"
|
|
end
|
|
end
|
|
|
|
def quoted_body
|
|
"[quote]\n#{from.pretty_name} said:\n\n#{body}\n[/quote]\n\n"
|
|
end
|
|
|
|
def send_email
|
|
if is_recipient? && !is_deleted? && to.receive_email_notifications? && to.email =~ /@/
|
|
UserMailer.dmail_notice(self).deliver_now
|
|
end
|
|
end
|
|
|
|
def is_automated?
|
|
from == User.system
|
|
end
|
|
|
|
def is_sender?
|
|
owner == from
|
|
end
|
|
|
|
def is_recipient?
|
|
owner == to
|
|
end
|
|
|
|
def autoreport_spam
|
|
if is_recipient? && SpamDetector.new(self).spam?
|
|
self.is_deleted = true
|
|
moderation_reports << ModerationReport.new(creator: User.system, reason: "Spam.")
|
|
end
|
|
end
|
|
|
|
def update_unread_dmail_count
|
|
return unless saved_change_to_id? || saved_change_to_is_read? || saved_change_to_is_deleted?
|
|
|
|
owner.with_lock do
|
|
unread_count = owner.dmails.active.unread.count
|
|
owner.update!(unread_dmail_count: unread_count)
|
|
end
|
|
end
|
|
|
|
def reportable_by?(user)
|
|
owner == user && is_recipient? && !is_automated? && !from.is_moderator?
|
|
end
|
|
|
|
def dtext_shortlink(key: false, **options)
|
|
key ? "dmail ##{id}/#{self.key}" : "dmail ##{id}"
|
|
end
|
|
|
|
def self.available_includes
|
|
[:owner, :to, :from]
|
|
end
|
|
end
|